2015-05-18 15:27:46 +02:00
|
|
|
/* "NETGEN", a netlist-specification tool for VLSI
|
|
|
|
|
Copyright (C) 1989, 1990 Massimo A. Sivilotti
|
|
|
|
|
Author's address: mass@csvax.cs.caltech.edu;
|
|
|
|
|
Caltech 256-80, Pasadena CA 91125.
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation (any version).
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; see the file copying. If not, write to
|
|
|
|
|
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
|
|
|
|
|
|
/* spice.c -- Input / output for SPICE and ESACAP formats */
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#if 0
|
|
|
|
|
#include <stdarg.h> /* what about varargs, like in pdutils.c ??? */
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-06-19 18:38:27 +02:00
|
|
|
#include <stdlib.h> /* for calloc(), free(), getenv() */
|
|
|
|
|
#ifndef IBMPC
|
|
|
|
|
#include <sys/types.h> /* for getpwnam() tilde expansion */
|
|
|
|
|
#include <pwd.h>
|
2015-05-18 15:27:46 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef TCL_NETGEN
|
|
|
|
|
#include <tcl.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "netgen.h"
|
|
|
|
|
#include "hash.h"
|
|
|
|
|
#include "objlist.h"
|
|
|
|
|
#include "netfile.h"
|
|
|
|
|
#include "print.h"
|
|
|
|
|
|
|
|
|
|
// Global storage for parameters from .PARAM
|
2016-06-23 16:13:18 +02:00
|
|
|
struct hashdict spiceparams;
|
2015-05-18 15:27:46 +02:00
|
|
|
|
2017-06-19 23:41:31 +02:00
|
|
|
// Global setting for auto-detect of empty subcircuits as
|
|
|
|
|
// black-box subcells.
|
|
|
|
|
int auto_blackbox = FALSE;
|
|
|
|
|
|
2016-10-17 23:47:27 +02:00
|
|
|
// Check if a token represents a numerical value (with
|
|
|
|
|
// units) or an expression. This is basically a hack
|
|
|
|
|
// to see if it either passes StringIsValue() or is
|
|
|
|
|
// enclosed in braces. Probably should attempt to
|
|
|
|
|
// parse the expression, to be pedantic. Not sure all
|
|
|
|
|
// expressions have to be in braces.
|
|
|
|
|
|
|
|
|
|
int StringIsValueOrExpression(char *token)
|
|
|
|
|
{
|
|
|
|
|
if (StringIsValue(token)) return TRUE;
|
|
|
|
|
else if (*token == '{') return TRUE;
|
|
|
|
|
else return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
void SpiceSubCell(struct nlist *tp, int IsSubCell)
|
|
|
|
|
{
|
|
|
|
|
struct objlist *ob;
|
|
|
|
|
int node, maxnode;
|
|
|
|
|
char *model;
|
|
|
|
|
struct tokstack *stackptr;
|
|
|
|
|
|
|
|
|
|
/* check to see that all children have been dumped */
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
|
|
|
if (ob->type == FIRSTPIN) {
|
|
|
|
|
struct nlist *tp2;
|
|
|
|
|
|
|
|
|
|
tp2 = LookupCellFile(ob->model.class, tp->file);
|
|
|
|
|
if ((tp2 != NULL) && !(tp2->dumped) && (tp2->class == CLASS_SUBCKT))
|
|
|
|
|
SpiceSubCell(tp2, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print preface, if it is a subcell */
|
|
|
|
|
if (IsSubCell) {
|
|
|
|
|
FlushString(".SUBCKT %s ",tp->name);
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
|
|
|
if (IsPortInPortlist(ob, tp)) FlushString("%d ", ob->node);
|
|
|
|
|
FlushString("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print names of all nodes, prefixed by comment character */
|
|
|
|
|
maxnode = 0;
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
|
|
|
if (ob->node > maxnode) maxnode = ob->node;
|
|
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
/* was: for (node = 0; node <= maxnode; node++) */
|
2015-05-18 15:27:46 +02:00
|
|
|
for (node = 1; node <= maxnode; node++)
|
|
|
|
|
FlushString("# %3d = %s\n", node, NodeName(tp, node));
|
|
|
|
|
|
|
|
|
|
/* traverse list of objects */
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
|
|
|
if (ob->type == FIRSTPIN) {
|
|
|
|
|
int drain_node, gate_node, source_node;
|
|
|
|
|
char spice_class;
|
|
|
|
|
struct nlist *tp2;
|
|
|
|
|
|
|
|
|
|
tp2 = LookupCellFile(ob->model.class, tp->file);
|
|
|
|
|
model = tp2->name;
|
|
|
|
|
|
|
|
|
|
/* Convert class numbers (defined in netgen.h) to SPICE classes */
|
|
|
|
|
switch (tp2->class) {
|
|
|
|
|
case CLASS_NMOS4: case CLASS_PMOS4: case CLASS_FET4:
|
|
|
|
|
case CLASS_NMOS: case CLASS_PMOS: case CLASS_FET3:
|
|
|
|
|
case CLASS_FET: case CLASS_ECAP:
|
|
|
|
|
spice_class = 'M';
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_NPN: case CLASS_PNP: case CLASS_BJT:
|
|
|
|
|
spice_class = 'Q';
|
|
|
|
|
break;
|
2019-01-10 02:26:38 +01:00
|
|
|
case CLASS_VSOURCE:
|
|
|
|
|
spice_class = 'V';
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_ISOURCE:
|
|
|
|
|
spice_class = 'I';
|
|
|
|
|
break;
|
2015-05-18 15:27:46 +02:00
|
|
|
case CLASS_RES: case CLASS_RES3:
|
|
|
|
|
spice_class = 'R';
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_DIODE:
|
|
|
|
|
spice_class = 'D';
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_INDUCTOR:
|
|
|
|
|
spice_class = 'L';
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_CAP: case CLASS_CAP3:
|
|
|
|
|
spice_class = 'C';
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_SUBCKT: case CLASS_MODULE:
|
|
|
|
|
spice_class = 'X';
|
|
|
|
|
break;
|
|
|
|
|
case CLASS_XLINE:
|
|
|
|
|
spice_class = 'T';
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Printf ("Bad device class found.\n");
|
|
|
|
|
continue; /* ignore it. . . */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FlushString("%c%s", spice_class, ob->instance.name);
|
|
|
|
|
|
|
|
|
|
/* Print out nodes. FETs switch node order */
|
|
|
|
|
|
|
|
|
|
switch (tp2->class) {
|
|
|
|
|
|
|
|
|
|
/* 3-terminal FET devices---handled specially */
|
|
|
|
|
case CLASS_NMOS: case CLASS_PMOS: case CLASS_FET3:
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
FlushString(" %s", ob->name); /* drain */
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
FlushString(" %s", ob->name); /* gate */
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
FlushString(" %s", ob->name); /* source */
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
if (tp2->class == CLASS_NMOS)
|
|
|
|
|
FlushString(" GND!"); /* default substrate */
|
|
|
|
|
else if (tp2->class == CLASS_PMOS)
|
|
|
|
|
FlushString(" VDD!"); /* default well */
|
|
|
|
|
else
|
|
|
|
|
FlushString(" BULK"); /* default bulk---unknown */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* All other devices have nodes in order of SPICE syntax */
|
|
|
|
|
default:
|
|
|
|
|
while (ob->next != NULL && ob->next->type >= FIRSTPIN) {
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
FlushString(" %s", ob->name);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-10 02:26:38 +01:00
|
|
|
/* caps, resistors, voltage and current sources, print out device value */
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
/* print out device type (model/subcircuit name) */
|
|
|
|
|
|
|
|
|
|
switch (tp2->class) {
|
|
|
|
|
case CLASS_CAP:
|
|
|
|
|
if (matchnocase(model, "c")) {
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
if (ob->type == PROPERTY) {
|
|
|
|
|
struct valuelist *vl;
|
|
|
|
|
int i;
|
|
|
|
|
for (i == 0;; i++) {
|
|
|
|
|
vl = &(ob->instance.props[i]);
|
|
|
|
|
if (vl->type == PROP_ENDLIST) break;
|
|
|
|
|
else if (vl->type == PROP_VALUE) {
|
|
|
|
|
FlushString(" %g", vl->value.dval);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
FlushString(" %s", model); /* semiconductor capacitor */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CLASS_RES:
|
|
|
|
|
if (matchnocase(model, "r")) {
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
if (ob->type == PROPERTY) {
|
|
|
|
|
struct valuelist *vl;
|
|
|
|
|
int i;
|
|
|
|
|
for (i == 0;; i++) {
|
|
|
|
|
vl = &(ob->instance.props[i]);
|
|
|
|
|
if (vl->type == PROP_ENDLIST) break;
|
|
|
|
|
else if (vl->type == PROP_VALUE) {
|
|
|
|
|
FlushString(" %g", vl->value.dval);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
FlushString(" %s", model); /* semiconductor resistor */
|
|
|
|
|
break;
|
|
|
|
|
|
2019-01-10 02:26:38 +01:00
|
|
|
case CLASS_VSOURCE:
|
|
|
|
|
case CLASS_ISOURCE:
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
if (ob->type == PROPERTY) {
|
|
|
|
|
struct valuelist *vl;
|
|
|
|
|
int i;
|
|
|
|
|
for (i == 0;; i++) {
|
|
|
|
|
vl = &(ob->instance.props[i]);
|
|
|
|
|
if (vl->type == PROP_ENDLIST) break;
|
|
|
|
|
else if (vl->type == PROP_VALUE) {
|
|
|
|
|
FlushString(" %g", vl->value.dval);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
default:
|
|
|
|
|
FlushString(" %s", model); /* everything else */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* write properties (if any) */
|
|
|
|
|
if (ob) ob = ob->next;
|
|
|
|
|
if (ob && ob->type == PROPERTY) {
|
|
|
|
|
struct valuelist *kv;
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; ; i++) {
|
|
|
|
|
kv = &(ob->instance.props[i]);
|
|
|
|
|
if (kv->type == PROP_ENDLIST) break;
|
|
|
|
|
switch (kv->type) {
|
|
|
|
|
case PROP_STRING:
|
|
|
|
|
FlushString(" %s=%s", kv->key, kv->value.string);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_INTEGER:
|
|
|
|
|
FlushString(" %s=%d", kv->key, kv->value.ival);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_DOUBLE:
|
|
|
|
|
case PROP_VALUE:
|
|
|
|
|
FlushString(" %s=%g", kv->key, kv->value.dval);
|
|
|
|
|
break;
|
|
|
|
|
case PROP_EXPRESSION:
|
|
|
|
|
FlushString(" %s=", kv->key);
|
|
|
|
|
stackptr = kv->value.stack;
|
|
|
|
|
while (stackptr->next != NULL)
|
|
|
|
|
stackptr = stackptr->next;
|
|
|
|
|
|
|
|
|
|
while (stackptr != NULL) {
|
|
|
|
|
switch (stackptr->toktype) {
|
|
|
|
|
case TOK_STRING:
|
|
|
|
|
FlushString("%s", stackptr->data.string);
|
|
|
|
|
break;
|
|
|
|
|
case TOK_DOUBLE:
|
|
|
|
|
FlushString("%d", stackptr->data.dvalue);
|
|
|
|
|
break;
|
|
|
|
|
case TOK_MULTIPLY:
|
|
|
|
|
FlushString("*");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_DIVIDE:
|
|
|
|
|
FlushString("/");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_PLUS:
|
|
|
|
|
FlushString("+");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_MINUS:
|
|
|
|
|
FlushString("-");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_FUNC_OPEN:
|
|
|
|
|
FlushString("(");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_FUNC_CLOSE:
|
|
|
|
|
FlushString(")");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_GT:
|
|
|
|
|
FlushString(">");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_LT:
|
|
|
|
|
FlushString("<");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_GE:
|
|
|
|
|
FlushString(">=");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_LE:
|
|
|
|
|
FlushString("<=");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_EQ:
|
|
|
|
|
FlushString("==");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_NE:
|
|
|
|
|
FlushString("!=");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_GROUP_OPEN:
|
|
|
|
|
FlushString("{");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_GROUP_CLOSE:
|
|
|
|
|
FlushString("}");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_FUNC_IF:
|
|
|
|
|
FlushString("IF(");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_FUNC_THEN:
|
|
|
|
|
case TOK_FUNC_ELSE:
|
|
|
|
|
FlushString(",");
|
|
|
|
|
break;
|
2015-08-10 18:08:05 +02:00
|
|
|
case TOK_SGL_QUOTE:
|
|
|
|
|
FlushString("'");
|
|
|
|
|
break;
|
|
|
|
|
case TOK_DBL_QUOTE:
|
|
|
|
|
FlushString("\"");
|
|
|
|
|
break;
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
stackptr = stackptr->last;
|
|
|
|
|
}
|
|
|
|
|
FlushString(" ");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FlushString("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsSubCell) FlushString(".ENDS\n");
|
|
|
|
|
tp->dumped = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SpiceCell(char *name, int fnum, char *filename)
|
|
|
|
|
{
|
|
|
|
|
struct nlist *tp;
|
|
|
|
|
char FileName[500];
|
|
|
|
|
|
|
|
|
|
tp = LookupCellFile(name, fnum);
|
|
|
|
|
|
|
|
|
|
if (tp == NULL) {
|
|
|
|
|
Printf ("No cell '%s' found.\n", name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filename == NULL || strlen(filename) == 0)
|
|
|
|
|
SetExtension(FileName, name, SPICE_EXTENSION);
|
|
|
|
|
else
|
|
|
|
|
SetExtension(FileName, filename, SPICE_EXTENSION);
|
|
|
|
|
|
|
|
|
|
if (!OpenFile(FileName, 80)) {
|
|
|
|
|
perror("ext(): Unable to open output file.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ClearDumpedList();
|
|
|
|
|
/* all spice decks begin with comment line */
|
|
|
|
|
FlushString("SPICE deck for cell %s written by Netgen %s.%s\n\n",
|
|
|
|
|
name, NETGEN_VERSION, NETGEN_REVISION);
|
|
|
|
|
SpiceSubCell(tp, 0);
|
|
|
|
|
CloseFile(FileName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
/* Routine to update instances with proper pin names, */
|
|
|
|
|
/* if the instances were called before the cell */
|
|
|
|
|
/* definition. */
|
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
int renamepins(struct hashlist *p, int file)
|
|
|
|
|
{
|
|
|
|
|
struct nlist *ptr, *tc;
|
|
|
|
|
struct objlist *ob, *ob2, *obp;
|
|
|
|
|
|
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
|
|
|
|
|
|
|
|
if (ptr->file != file)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
|
|
|
|
|
if (ob->type == FIRSTPIN) {
|
|
|
|
|
tc = LookupCellFile(ob->model.class, file);
|
|
|
|
|
obp = ob;
|
|
|
|
|
for (ob2 = tc->cell; ob2 != NULL; ob2 = ob2->next) {
|
|
|
|
|
if (ob2->type != PORT) break;
|
2016-06-23 16:13:18 +02:00
|
|
|
else if ((obp->type < FIRSTPIN) || (obp->type == FIRSTPIN && obp != ob)) {
|
|
|
|
|
Fprintf(stderr, "Pin count mismatch between cell and instance of %s\n",
|
|
|
|
|
tc->name);
|
|
|
|
|
InputParseError(stderr);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
if (!matchnocase(ob2->name, obp->name + strlen(obp->instance.name) + 1)) {
|
|
|
|
|
// Printf("Cell %s pin correspondence: %s vs. %s\n",
|
|
|
|
|
// tc->name, obp->name, ob2->name);
|
|
|
|
|
FREE(obp->name);
|
|
|
|
|
obp->name = (char *)MALLOC(strlen(obp->instance.name)
|
|
|
|
|
+ strlen(ob2->name) + 2);
|
|
|
|
|
sprintf(obp->name, "%s/%s", obp->instance.name, ob2->name);
|
|
|
|
|
}
|
|
|
|
|
obp = obp->next;
|
|
|
|
|
if (obp == NULL) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 14:35:51 +02:00
|
|
|
/* If any pins are marked unconnected, see if there are */
|
|
|
|
|
/* other pins of the same name that have connections. */
|
|
|
|
|
/* Also remove any unconnected globals (just for cleanup) */
|
|
|
|
|
|
|
|
|
|
void CleanupSubcell() {
|
|
|
|
|
int maxnode = 0;
|
2017-06-19 23:41:31 +02:00
|
|
|
int has_devices = FALSE;
|
2015-08-21 14:35:51 +02:00
|
|
|
struct objlist *sobj, *nobj, *lobj, *pobj;
|
|
|
|
|
|
|
|
|
|
if (CurrentCell == NULL) return;
|
|
|
|
|
|
|
|
|
|
for (sobj = CurrentCell->cell; sobj; sobj = sobj->next)
|
|
|
|
|
if (sobj->node > maxnode)
|
|
|
|
|
maxnode = sobj->node + 1;
|
|
|
|
|
|
|
|
|
|
lobj = NULL;
|
|
|
|
|
for (sobj = CurrentCell->cell; sobj != NULL;) {
|
|
|
|
|
nobj = sobj->next;
|
2017-06-19 23:41:31 +02:00
|
|
|
if (sobj->type == FIRSTPIN)
|
|
|
|
|
has_devices = TRUE;
|
2015-08-21 14:35:51 +02:00
|
|
|
if (sobj->node < 0) {
|
|
|
|
|
if (IsGlobal(sobj)) {
|
|
|
|
|
if (lobj != NULL)
|
|
|
|
|
lobj->next = sobj->next;
|
|
|
|
|
else
|
|
|
|
|
CurrentCell->cell = sobj->next;
|
|
|
|
|
FreeObjectAndHash(sobj, CurrentCell);
|
|
|
|
|
}
|
|
|
|
|
else if (IsPort(sobj) && sobj->model.port == PROXY)
|
|
|
|
|
sobj->node = maxnode++;
|
|
|
|
|
else if (IsPort(sobj)) {
|
|
|
|
|
for (pobj = CurrentCell->cell; pobj && (pobj->type == PORT);
|
|
|
|
|
pobj = pobj->next) {
|
|
|
|
|
if (pobj == sobj) continue;
|
|
|
|
|
if (matchnocase(pobj->name, sobj->name) && pobj->node >= 0) {
|
|
|
|
|
sobj->node = pobj->node;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lobj = sobj;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
lobj = sobj;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
lobj = sobj;
|
|
|
|
|
sobj = nobj;
|
|
|
|
|
}
|
2017-06-19 23:41:31 +02:00
|
|
|
if ((has_devices == FALSE) && (auto_blackbox == TRUE))
|
|
|
|
|
SetClass(CLASS_MODULE);
|
2015-08-21 14:35:51 +02:00
|
|
|
}
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
/* Push a subcircuit name onto the stack */
|
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void PushStack(char *cellname, struct cellstack **top)
|
|
|
|
|
{
|
|
|
|
|
struct cellstack *newstack;
|
|
|
|
|
|
|
|
|
|
newstack = (struct cellstack *)CALLOC(1, sizeof(struct cellstack));
|
|
|
|
|
newstack->cellname = cellname;
|
|
|
|
|
newstack->next = *top;
|
|
|
|
|
*top = newstack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
/* Pop a subcircuit name off of the stack */
|
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void PopStack(struct cellstack **top)
|
|
|
|
|
{
|
|
|
|
|
struct cellstack *stackptr;
|
|
|
|
|
|
|
|
|
|
stackptr = *top;
|
|
|
|
|
if (!stackptr) return;
|
|
|
|
|
*top = stackptr->next;
|
|
|
|
|
FREE(stackptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Forward declaration */
|
|
|
|
|
extern void IncludeSpice(char *, int, struct cellstack **, int);
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
/* Read a SPICE deck */
|
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
|
|
|
|
int blackbox)
|
|
|
|
|
{
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
int cdnum = 1, rdnum = 1;
|
2015-05-18 15:27:46 +02:00
|
|
|
int warnings = 0, update = 0, hasports = 0;
|
2015-08-31 15:33:31 +02:00
|
|
|
char *eqptr, devtype, in_subckt;
|
2015-05-18 15:27:46 +02:00
|
|
|
struct keyvalue *kvlist = NULL;
|
|
|
|
|
char inst[256], model[256], instname[256];
|
|
|
|
|
struct nlist *tp;
|
|
|
|
|
struct objlist *parent, *sobj, *nobj, *lobj, *pobj;
|
|
|
|
|
|
|
|
|
|
inst[255] = '\0';
|
|
|
|
|
model[255] = '\0';
|
|
|
|
|
instname[255] = '\0';
|
2015-08-31 15:33:31 +02:00
|
|
|
in_subckt = (char)0;
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
while (!EndParseFile()) {
|
2015-08-21 14:35:51 +02:00
|
|
|
|
2018-04-12 23:09:10 +02:00
|
|
|
SkipTok(NULL); /* get the next token */
|
2015-08-21 04:11:58 +02:00
|
|
|
if ((EndParseFile()) && (nexttok == NULL)) break;
|
2015-05-18 15:27:46 +02:00
|
|
|
|
2018-04-12 23:09:10 +02:00
|
|
|
if (nexttok[0] == '*') SkipNewLine(NULL);
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
else if (matchnocase(nexttok, ".SUBCKT")) {
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) {
|
|
|
|
|
Fprintf(stderr, "Badly formed .subkt line\n");
|
|
|
|
|
goto skip_ends;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-31 15:33:31 +02:00
|
|
|
if (in_subckt == (char)1) {
|
|
|
|
|
Fprintf(stderr, "Missing .ENDS statement on subcircuit.\n");
|
|
|
|
|
InputParseError(stderr);
|
|
|
|
|
}
|
|
|
|
|
in_subckt = (char)1;
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
/* Save pointer to current cell */
|
|
|
|
|
if (CurrentCell != NULL)
|
|
|
|
|
parent = CurrentCell->cell;
|
|
|
|
|
else
|
|
|
|
|
parent = NULL;
|
|
|
|
|
|
|
|
|
|
/* Check for existence of the cell. We may need to rename it. */
|
|
|
|
|
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
tp = LookupCellFile(nexttok, filenum);
|
|
|
|
|
|
|
|
|
|
/* Check for name conflict with duplicate cell names */
|
|
|
|
|
/* This may mean that the cell was used before it was */
|
|
|
|
|
/* defined, but CDL files sometimes just redefine the */
|
|
|
|
|
/* same cell over and over. So check if it's empty. */
|
|
|
|
|
|
|
|
|
|
if ((tp != NULL) && (tp->class != CLASS_MODULE)) {
|
|
|
|
|
int n;
|
|
|
|
|
char *ds;
|
|
|
|
|
|
2016-05-05 17:27:23 +02:00
|
|
|
// NOTE: Use this to ignore the new definition---should be
|
|
|
|
|
// an option to netgen.
|
|
|
|
|
/* goto skip_ends; */
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
ds = strrchr(model, '[');
|
|
|
|
|
if ((ds != NULL) && (*(ds + 1) == '['))
|
|
|
|
|
sscanf(ds + 2, "%d", &n);
|
|
|
|
|
else {
|
|
|
|
|
ds = model + strlen(model);
|
|
|
|
|
sprintf(ds, "[[0]]");
|
|
|
|
|
n = -1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 03:49:55 +02:00
|
|
|
Printf("Duplicate cell %s in file\n", nexttok);
|
2016-05-16 16:50:12 +02:00
|
|
|
tp->flags |= CELL_DUPLICATE;
|
2015-05-18 15:27:46 +02:00
|
|
|
while (tp != NULL) {
|
|
|
|
|
n++;
|
|
|
|
|
/* Append "[[n]]" to the preexisting model name to force uniqueness */
|
|
|
|
|
sprintf(ds, "[[%d]]", n);
|
|
|
|
|
tp = LookupCellFile(model, filenum);
|
|
|
|
|
}
|
2015-09-30 03:49:55 +02:00
|
|
|
Printf("Renaming original cell to %s\n", model);
|
2015-05-18 15:27:46 +02:00
|
|
|
InstanceRename(nexttok, model, filenum);
|
|
|
|
|
CellRehash(nexttok, model, filenum);
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(nexttok, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
tp = LookupCellFile(nexttok, filenum);
|
|
|
|
|
}
|
|
|
|
|
else if (tp != NULL) { /* Make a new definition for an empty cell */
|
|
|
|
|
FreePorts(nexttok);
|
|
|
|
|
CellDelete(nexttok, filenum); /* This removes any PLACEHOLDER flag */
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
tp = LookupCellFile(model, filenum);
|
|
|
|
|
update = 1; /* Will need to update existing instances */
|
|
|
|
|
}
|
|
|
|
|
else if (tp == NULL) { /* Completely new cell, no name conflict */
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
tp = LookupCellFile(model, filenum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hasports = 0;
|
|
|
|
|
if (tp != NULL) {
|
|
|
|
|
|
|
|
|
|
PushStack(tp->name, CellStackPtr);
|
|
|
|
|
|
|
|
|
|
/* Tokens on the rest of the line are ports or */
|
|
|
|
|
/* properties. Treat everything with an "=" as a */
|
|
|
|
|
/* property, all others as ports. "M=" is *not* a */
|
|
|
|
|
/* valid property meaning "number of" in a SUBCKT */
|
|
|
|
|
/* line, and if it exists, it should be recorded as */
|
|
|
|
|
/* a property, and (to be done) NOT treated as */
|
|
|
|
|
/* referring to number of devices in a subcircuit */
|
|
|
|
|
/* call. */
|
|
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
while (nexttok != NULL) {
|
|
|
|
|
|
|
|
|
|
// Because of somebody's stupid meddling with
|
|
|
|
|
// SPICE syntax, we have to check for and ignore
|
|
|
|
|
// any use of the keyword "PARAMS:"
|
|
|
|
|
|
|
|
|
|
if (!strcasecmp(nexttok, "PARAMS:")) {
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL) {
|
|
|
|
|
*eqptr = '\0';
|
|
|
|
|
// Only String properties allowed
|
|
|
|
|
PropertyString(tp->name, filenum, nexttok, 0, eqptr + 1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Port(nexttok);
|
|
|
|
|
hasports = 1;
|
|
|
|
|
}
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
2017-06-21 04:50:31 +02:00
|
|
|
SetClass((blackbox) ? CLASS_MODULE : CLASS_SUBCKT);
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
if (hasports == 0) {
|
|
|
|
|
// If the cell defines no ports, then create a proxy
|
|
|
|
|
Port((char *)NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy all global nodes from parent into child cell */
|
|
|
|
|
for (sobj = parent; sobj != NULL; sobj = sobj->next) {
|
|
|
|
|
if (IsGlobal(sobj)) {
|
|
|
|
|
Global(sobj->name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In the blackbox case, don't read the cell contents */
|
|
|
|
|
if (blackbox) goto skip_ends;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
skip_ends:
|
|
|
|
|
/* There was an error, so skip to the end of the */
|
|
|
|
|
/* subcircuit definition */
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
SpiceSkipNewLine();
|
2018-04-12 23:09:10 +02:00
|
|
|
SkipTok(NULL);
|
2015-05-18 15:27:46 +02:00
|
|
|
if (EndParseFile()) break;
|
2015-08-31 15:33:31 +02:00
|
|
|
if (matchnocase(nexttok, ".ENDS")) {
|
|
|
|
|
in_subckt = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-31 15:33:31 +02:00
|
|
|
else if (matchnocase(nexttok, ".ENDS")) {
|
2015-05-18 15:27:46 +02:00
|
|
|
|
2015-08-21 14:35:51 +02:00
|
|
|
CleanupSubcell();
|
2015-05-18 15:27:46 +02:00
|
|
|
EndCell();
|
|
|
|
|
|
2015-08-31 15:33:31 +02:00
|
|
|
if (in_subckt == (char)0) {
|
|
|
|
|
Fprintf(stderr, ".ENDS occurred outside of a subcircuit!\n");
|
|
|
|
|
InputParseError(stderr);
|
|
|
|
|
}
|
|
|
|
|
in_subckt = (char)0;
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
if (*CellStackPtr) PopStack(CellStackPtr);
|
|
|
|
|
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
2018-04-12 23:09:10 +02:00
|
|
|
SkipNewLine(NULL);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
else if (matchnocase(nexttok, ".MODEL")) {
|
|
|
|
|
unsigned char class = CLASS_SUBCKT;
|
|
|
|
|
struct nlist *ncell;
|
|
|
|
|
|
|
|
|
|
/* A .MODEL statement can refine our knowledge of whether a Q or */
|
|
|
|
|
/* M device is type "n" or "p", allowing us to properly translate */
|
|
|
|
|
/* to other formats (e.g., .sim). If there are no .MODEL */
|
|
|
|
|
/* statements, the "equate classes" command must be used. */
|
|
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) continue; /* Ignore if no model name */
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
|
|
|
|
|
if (!strcasecmp(nexttok, "NMOS")) {
|
|
|
|
|
class = CLASS_NMOS;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "PMOS")) {
|
|
|
|
|
class = CLASS_PMOS;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "PNP")) {
|
|
|
|
|
class = CLASS_PNP;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "NPN")) {
|
|
|
|
|
class = CLASS_NPN;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "NPN")) {
|
|
|
|
|
class = CLASS_NPN;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "D")) {
|
|
|
|
|
class = CLASS_DIODE;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "R")) {
|
|
|
|
|
class = CLASS_RES;
|
|
|
|
|
}
|
2019-01-10 02:26:38 +01:00
|
|
|
else if (!strcasecmp(nexttok, "V")) {
|
|
|
|
|
class = CLASS_VSOURCE;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "I")) {
|
|
|
|
|
class = CLASS_ISOURCE;
|
|
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
else if (!strcasecmp(nexttok, "C")) {
|
|
|
|
|
class = CLASS_CAP;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcasecmp(nexttok, "L")) {
|
|
|
|
|
class = CLASS_INDUCTOR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert class of "model" to "class" */
|
|
|
|
|
if (class != CLASS_SUBCKT) {
|
|
|
|
|
ncell = LookupCellFile(model, filenum);
|
|
|
|
|
if (ncell) ncell->class = class;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle some commonly-used cards
|
|
|
|
|
|
|
|
|
|
else if (matchnocase(nexttok, ".GLOBAL")) {
|
|
|
|
|
while (nexttok != NULL) {
|
|
|
|
|
int numnodes = 0;
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
|
|
|
|
|
// First handle backward references
|
|
|
|
|
if (CurrentCell != NULL)
|
|
|
|
|
numnodes = ChangeScopeCurrent(nexttok, NODE, GLOBAL);
|
|
|
|
|
|
|
|
|
|
/* If there are no backward references, then treat it */
|
|
|
|
|
/* as a forward reference */
|
|
|
|
|
|
|
|
|
|
if (numnodes == 0) {
|
|
|
|
|
// If there is no current cell, make one
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
Global(nexttok);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
else if (matchnocase(nexttok, ".INCLUDE")) {
|
2017-06-19 18:38:27 +02:00
|
|
|
char *iname, *iptr, *quotptr, *pathend, *userpath = NULL;
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) continue; /* Ignore if no filename */
|
|
|
|
|
|
|
|
|
|
// Any file included in another SPICE file needs to be
|
|
|
|
|
// interpreted relative to the path of the parent SPICE file,
|
|
|
|
|
// unless it's an absolute pathname.
|
|
|
|
|
|
|
|
|
|
pathend = strrchr(fname, '/');
|
|
|
|
|
iptr = nexttok;
|
|
|
|
|
while (*iptr == '\'' || *iptr == '\"' || *iptr == '`') iptr++;
|
2017-06-19 18:38:27 +02:00
|
|
|
if ((pathend != NULL) && (*iptr != '/') && (*iptr != '~')) {
|
2015-05-18 15:27:46 +02:00
|
|
|
*pathend = '\0';
|
|
|
|
|
iname = (char *)MALLOC(strlen(fname) + strlen(iptr) + 2);
|
|
|
|
|
sprintf(iname, "%s/%s", fname, iptr);
|
|
|
|
|
*pathend = '/';
|
|
|
|
|
}
|
2017-06-19 18:38:27 +02:00
|
|
|
#ifndef IBMPC
|
|
|
|
|
else if ((*iptr == '~') && (*(iptr + 1) == '/')) {
|
|
|
|
|
/* For ~/<path>, substitute tilde from $HOME */
|
|
|
|
|
userpath = getenv("HOME");
|
|
|
|
|
iname = (char *)MALLOC(strlen(userpath) + strlen(iptr));
|
2017-06-22 14:12:41 +02:00
|
|
|
sprintf(iname, "%s%s", userpath, iptr + 1);
|
2017-06-19 18:38:27 +02:00
|
|
|
}
|
|
|
|
|
else if (*iptr == '~') {
|
|
|
|
|
/* For ~<user>/<path>, substitute tilde from getpwnam() */
|
|
|
|
|
struct passwd *passwd;
|
|
|
|
|
char *pathstart;
|
|
|
|
|
pathstart = strchr(iptr, '/');
|
|
|
|
|
if (pathstart) *pathstart = '\0';
|
|
|
|
|
passwd = getpwnam(iptr + 1);
|
|
|
|
|
if (passwd != NULL) {
|
|
|
|
|
userpath = passwd->pw_dir;
|
|
|
|
|
if (pathstart) {
|
|
|
|
|
*pathstart = '/';
|
|
|
|
|
iname = (char *)MALLOC(strlen(userpath) + strlen(pathstart) + 1);
|
2017-06-22 14:12:41 +02:00
|
|
|
sprintf(iname, "%s%s", userpath, pathstart);
|
2017-06-19 18:38:27 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Almost certainly an error, but make the substitution anyway */
|
|
|
|
|
iname = STRDUP(userpath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Probably an error, but copy the filename verbatim */
|
|
|
|
|
iname = STRDUP(iptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2015-05-18 15:27:46 +02:00
|
|
|
else
|
|
|
|
|
iname = STRDUP(iptr);
|
|
|
|
|
|
|
|
|
|
// Eliminate any single or double quotes around the filename
|
|
|
|
|
iptr = iname;
|
|
|
|
|
quotptr = iptr;
|
|
|
|
|
while (*quotptr != '\'' && *quotptr != '\"' && *quotptr != '`' &&
|
|
|
|
|
*quotptr != '\0' && *quotptr != '\n') quotptr++;
|
|
|
|
|
if (*quotptr == '\'' || *quotptr == '\"' || *quotptr == '`') *quotptr = '\0';
|
|
|
|
|
|
|
|
|
|
IncludeSpice(iptr, filenum, CellStackPtr, blackbox);
|
|
|
|
|
FREE(iname);
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (matchnocase(nexttok, ".PARAM")) {
|
|
|
|
|
|
|
|
|
|
// Pick up key:value pairs and store in current cell
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Parse for parameters used in expressions. Save */
|
|
|
|
|
/* parameters in the "spiceparams" hash table. */
|
|
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
struct property *kl = NULL;
|
|
|
|
|
|
|
|
|
|
*eqptr = '\0';
|
|
|
|
|
kl = NewProperty();
|
|
|
|
|
kl->key = strsave(nexttok);
|
|
|
|
|
kl->idx = 0;
|
|
|
|
|
kl->type = PROP_STRING;
|
2016-12-12 17:32:07 +01:00
|
|
|
kl->slop.dval = 0.0;
|
2015-05-18 15:27:46 +02:00
|
|
|
kl->pdefault.string = strsave(eqptr + 1);
|
2016-06-23 16:13:18 +02:00
|
|
|
HashPtrInstall(nexttok, kl, &spiceparams);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 15:30:27 +01:00
|
|
|
/* Ignore anything in a .CONTROL ... .ENDC block */
|
|
|
|
|
else if (matchnocase(nexttok, ".CONTROL")) {
|
|
|
|
|
while (1) {
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
SkipTok(NULL);
|
|
|
|
|
if (EndParseFile()) break;
|
|
|
|
|
if (matchnocase(nexttok, ".ENDC"))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
// Blackbox (library) mode---parse only subcircuits and models;
|
|
|
|
|
// ignore all components.
|
|
|
|
|
|
|
|
|
|
else if (blackbox) {
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (toupper(nexttok[0]) == 'Q') {
|
|
|
|
|
char emitter[100], base[100], collector[100];
|
|
|
|
|
emitter[99] = '\0';
|
|
|
|
|
base[99] = '\0';
|
|
|
|
|
collector[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(collector, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(base, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(emitter, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(collector, CurrentCell) == NULL) Node(collector);
|
|
|
|
|
if (LookupObject(base, CurrentCell) == NULL) Node(base);
|
|
|
|
|
if (LookupObject(emitter, CurrentCell) == NULL) Node(emitter);
|
|
|
|
|
|
|
|
|
|
/* Read the device model */
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Parse for M and other parameters */
|
|
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("collector");
|
|
|
|
|
Port("base");
|
|
|
|
|
Port("emitter");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_BJT);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 3) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a BJT.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
snprintf(instname, 255, "%s%s", model, inst);
|
|
|
|
|
Cell(instname, model, collector, base, emitter);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'M') {
|
|
|
|
|
char drain[100], gate[100], source[100], bulk[100];
|
|
|
|
|
drain[99] = '\0';
|
|
|
|
|
gate[99] = '\0';
|
|
|
|
|
source[99] = '\0';
|
|
|
|
|
bulk[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(drain, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(gate, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(source, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(drain, CurrentCell) == NULL) Node(drain);
|
|
|
|
|
if (LookupObject(gate, CurrentCell) == NULL) Node(gate);
|
|
|
|
|
if (LookupObject(source, CurrentCell) == NULL) Node(source);
|
|
|
|
|
|
|
|
|
|
/* handle the substrate node */
|
|
|
|
|
strncpy(bulk, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (LookupObject(bulk, CurrentCell) == NULL) Node(bulk);
|
|
|
|
|
|
|
|
|
|
/* Read the device model */
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Parse for parameters; treat "M" separately */
|
|
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Treat each different model name as a separate device class */
|
|
|
|
|
/* The model name is prefixed with "M/" so that we know this is a */
|
|
|
|
|
/* SPICE transistor. */
|
|
|
|
|
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("drain");
|
|
|
|
|
Port("gate");
|
|
|
|
|
Port("source");
|
|
|
|
|
Port("bulk");
|
2016-06-24 04:27:34 +02:00
|
|
|
PropertyDouble(model, filenum, "L", 0.01, 0.0);
|
|
|
|
|
PropertyDouble(model, filenum, "W", 0.01, 0.0);
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_FET);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 4) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a MOSFET.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
snprintf(instname, 255, "%s%s", model, inst);
|
|
|
|
|
Cell(instname, model, drain, gate, source, bulk);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'C') { /* 2-port capacitors */
|
|
|
|
|
int usemodel = 0;
|
|
|
|
|
|
|
|
|
|
if (IgnoreRC) {
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
char ctop[100], cbot[100];
|
|
|
|
|
ctop[99] = '\0';
|
|
|
|
|
cbot[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(ctop, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(cbot, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(ctop, CurrentCell) == NULL) Node(ctop);
|
|
|
|
|
if (LookupObject(cbot, CurrentCell) == NULL) Node(cbot);
|
|
|
|
|
|
|
|
|
|
/* Get capacitor value (if present), save as property "value" */
|
|
|
|
|
if (nexttok != NULL) {
|
2016-10-17 23:47:27 +02:00
|
|
|
if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Semiconductor (modeled) capacitor. But first need to make */
|
|
|
|
|
/* sure that this does not start the list of parameters. */
|
|
|
|
|
|
|
|
|
|
model[0] = '\0';
|
|
|
|
|
if ((nexttok != NULL) && ((eqptr = strchr(nexttok, '=')) == NULL))
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
|
|
|
|
|
/* Any other device properties? */
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL) {
|
|
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
else if (!strncmp(nexttok, "$[", 2)) {
|
|
|
|
|
// Support for CDL modeled capacitor format
|
|
|
|
|
snprintf(model, 99, "%s", nexttok + 2);
|
|
|
|
|
if ((eqptr = strchr(model, ']')) != NULL)
|
|
|
|
|
*eqptr = '\0';
|
|
|
|
|
}
|
2016-10-17 23:47:27 +02:00
|
|
|
else if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
// Suport for value passed to modeled capacitor
|
|
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (model[0] == '\0')
|
|
|
|
|
strcpy(model, "c"); /* Use default capacitor model */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("top");
|
|
|
|
|
Port("bottom");
|
|
|
|
|
PropertyValue(model, filenum, "value", 0.01, 0.0);
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_CAP);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 2) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
|
|
|
|
"capacitor.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
usemodel = 1;
|
|
|
|
|
}
|
|
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
snprintf(instname, 255, "%s%s", model, inst);
|
|
|
|
|
if (usemodel)
|
|
|
|
|
Cell(instname, model, ctop, cbot);
|
|
|
|
|
else
|
|
|
|
|
Cap((*CellStackPtr)->cellname, instname, ctop, cbot);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'R') { /* 2-port resistors */
|
|
|
|
|
int usemodel = 0;
|
|
|
|
|
|
|
|
|
|
if (IgnoreRC) {
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
char rtop[100], rbot[100];
|
|
|
|
|
rtop[99] = '\0';
|
|
|
|
|
rbot[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(rtop, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(rbot, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(rtop, CurrentCell) == NULL) Node(rtop);
|
|
|
|
|
if (LookupObject(rbot, CurrentCell) == NULL) Node(rbot);
|
|
|
|
|
|
|
|
|
|
/* Get resistor value (if present); save as property "value" */
|
|
|
|
|
|
|
|
|
|
if (nexttok != NULL) {
|
2016-10-17 23:47:27 +02:00
|
|
|
if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Semiconductor (modeled) resistor. But first need to make */
|
|
|
|
|
/* sure that this does not start the list of parameters. */
|
|
|
|
|
|
|
|
|
|
model[0] = '\0';
|
|
|
|
|
if ((nexttok != NULL) && ((eqptr = strchr(nexttok, '=')) == NULL))
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
|
|
|
|
|
/* Any other device properties? */
|
|
|
|
|
while (nexttok != NULL) {
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL) {
|
|
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
else if (!strncmp(nexttok, "$[", 2)) {
|
|
|
|
|
// Support for CDL modeled resistor format
|
|
|
|
|
snprintf(model, 99, "%s", nexttok + 2);
|
|
|
|
|
if ((eqptr = strchr(model, ']')) != NULL)
|
|
|
|
|
*eqptr = '\0';
|
|
|
|
|
}
|
2016-10-17 23:47:27 +02:00
|
|
|
else if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
// Suport for value passed to modeled resistor
|
|
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (model[0] != '\0')
|
|
|
|
|
{
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("end_a");
|
|
|
|
|
Port("end_b");
|
|
|
|
|
PropertyValue(model, filenum, "value", 0.01, 0.0);
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_RES);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 2) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
|
|
|
|
"resistor.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
usemodel = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
strcpy(model, "r"); /* Use default resistor model */
|
|
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
snprintf(instname, 255, "%s%s", model, inst);
|
|
|
|
|
if (usemodel)
|
|
|
|
|
Cell(instname, model, rtop, rbot);
|
|
|
|
|
else
|
|
|
|
|
Res((*CellStackPtr)->cellname, instname, rtop, rbot);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'D') { /* diode */
|
|
|
|
|
char cathode[100], anode[100];
|
|
|
|
|
cathode[99] = '\0';
|
|
|
|
|
anode[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(anode, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(cathode, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(anode, CurrentCell) == NULL) Node(anode);
|
|
|
|
|
if (LookupObject(cathode, CurrentCell) == NULL) Node(cathode);
|
|
|
|
|
|
|
|
|
|
/* Read the device model */
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Parse for M and other parameters */
|
|
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("anode");
|
|
|
|
|
Port("cathode");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_DIODE);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 2) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a diode.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
snprintf(instname, 255, "%s%s", model, inst);
|
|
|
|
|
Cell(instname, model, anode, cathode);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'T') { /* transmission line */
|
|
|
|
|
int usemodel = 0;
|
|
|
|
|
|
|
|
|
|
if (IgnoreRC) {
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
char node1[100], node2[100], node3[100], node4[100];
|
|
|
|
|
node1[99] = '\0';
|
|
|
|
|
node2[99] = '\0';
|
|
|
|
|
node3[99] = '\0';
|
|
|
|
|
node4[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(node1, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(node2, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(node3, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(node4, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(node1, CurrentCell) == NULL) Node(node1);
|
|
|
|
|
if (LookupObject(node2, CurrentCell) == NULL) Node(node2);
|
|
|
|
|
if (LookupObject(node3, CurrentCell) == NULL) Node(node3);
|
|
|
|
|
if (LookupObject(node4, CurrentCell) == NULL) Node(node4);
|
|
|
|
|
|
|
|
|
|
/* Lossy (modeled) transmission line. But first need to make */
|
|
|
|
|
/* sure that this does not start the list of parameters. */
|
|
|
|
|
|
|
|
|
|
model[0] = '\0';
|
|
|
|
|
if ((nexttok != NULL) && ((eqptr = strchr(nexttok, '=')) == NULL))
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
|
|
|
|
|
/* Any other device properties? */
|
|
|
|
|
while (nexttok != NULL) {
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL) {
|
|
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (model[0] != '\0')
|
|
|
|
|
{
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("node1");
|
|
|
|
|
Port("node2");
|
|
|
|
|
Port("node3");
|
|
|
|
|
Port("node4");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_XLINE);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 4) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
|
|
|
|
"transmission line.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
usemodel = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
strcpy(model, "t"); /* Use default xline model */
|
|
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
snprintf(instname, 255, "%s%s", model, inst);
|
2015-05-18 15:27:46 +02:00
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
if (usemodel)
|
|
|
|
|
Cell(instname, model, node1, node2, node3, node4);
|
|
|
|
|
else
|
|
|
|
|
XLine((*CellStackPtr)->cellname, instname, node1, node2,
|
2015-05-18 15:27:46 +02:00
|
|
|
node3, node4);
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
pobj = LinkProperties(model, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'L') { /* inductor */
|
|
|
|
|
char end_a[100], end_b[100];
|
|
|
|
|
int usemodel = 0;
|
|
|
|
|
end_a[99] = '\0';
|
|
|
|
|
end_b[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(end_a, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(end_b, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(end_a, CurrentCell) == NULL) Node(end_a);
|
|
|
|
|
if (LookupObject(end_b, CurrentCell) == NULL) Node(end_b);
|
|
|
|
|
|
|
|
|
|
/* Get inductance value (if present); save as property "value" */
|
|
|
|
|
|
|
|
|
|
if (nexttok != NULL) {
|
2016-10-17 23:47:27 +02:00
|
|
|
if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Semiconductor (modeled) inductor. But first need to make */
|
|
|
|
|
/* sure that this does not start the list of parameters. */
|
|
|
|
|
|
|
|
|
|
model[0] = '\0';
|
|
|
|
|
if ((nexttok != NULL) && ((eqptr = strchr(nexttok, '=')) == NULL))
|
|
|
|
|
snprintf(model, 99, "%s", nexttok);
|
|
|
|
|
|
|
|
|
|
/* Any other device properties? */
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Parse for M and other parameters */
|
|
|
|
|
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (model[0] != '\0')
|
|
|
|
|
{
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("end_a");
|
|
|
|
|
Port("end_b");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_INDUCTOR);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 2) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for an "
|
|
|
|
|
"inductor.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
usemodel = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
strcpy(model, "l"); /* Use default inductor model */
|
|
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
snprintf(instname, 255, "%s%s", model, inst);
|
|
|
|
|
if (usemodel)
|
|
|
|
|
Cell(instname, model, end_a, end_b);
|
|
|
|
|
else
|
|
|
|
|
Inductor((*CellStackPtr)->cellname, instname, end_a, end_b);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The following SPICE components are treated as */
|
|
|
|
|
/* black-box subcircuits (class MODULE): V, I, E */
|
|
|
|
|
|
|
|
|
|
else if (toupper(nexttok[0]) == 'V') { /* voltage source */
|
|
|
|
|
char pos[100], neg[100];
|
|
|
|
|
pos[99] = '\0';
|
|
|
|
|
neg[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(pos, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(neg, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(pos, CurrentCell) == NULL) Node(pos);
|
|
|
|
|
if (LookupObject(neg, CurrentCell) == NULL) Node(neg);
|
|
|
|
|
|
|
|
|
|
/* Any device properties? */
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*eqptr = '\0';
|
|
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
|
|
|
|
}
|
2016-10-17 23:47:27 +02:00
|
|
|
else if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
strcpy(model, "vsrc"); /* Default voltage source */
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("pos");
|
|
|
|
|
Port("neg");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2019-01-10 02:26:38 +01:00
|
|
|
SetClass(CLASS_VSOURCE);
|
2015-05-18 15:27:46 +02:00
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 2) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
|
|
|
|
"voltage source.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
Cell(instname, model, pos, neg);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
2016-06-24 04:27:34 +02:00
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'I') { /* current source */
|
|
|
|
|
char pos[100], neg[100];
|
|
|
|
|
pos[99] = '\0';
|
|
|
|
|
neg[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(pos, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(neg, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(pos, CurrentCell) == NULL) Node(pos);
|
|
|
|
|
if (LookupObject(neg, CurrentCell) == NULL) Node(neg);
|
|
|
|
|
|
|
|
|
|
/* Any device properties? */
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*eqptr = '\0';
|
|
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
|
|
|
|
}
|
2016-10-17 23:47:27 +02:00
|
|
|
else if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
strcpy(model, "isrc"); /* Default current source */
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("pos");
|
|
|
|
|
Port("neg");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2019-01-10 02:26:38 +01:00
|
|
|
SetClass(CLASS_ISOURCE);
|
2015-05-18 15:27:46 +02:00
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 2) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
|
|
|
|
"current source.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
Cell(instname, model, pos, neg);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
2016-06-24 04:27:34 +02:00
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
else if (toupper(nexttok[0]) == 'E') { /* controlled voltage source */
|
|
|
|
|
char pos[100], neg[100], ctrlp[100], ctrln[100];
|
|
|
|
|
pos[99] = '\0';
|
|
|
|
|
neg[99] = '\0';
|
|
|
|
|
ctrlp[99] = '\0';
|
|
|
|
|
ctrln[99] = '\0';
|
|
|
|
|
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
strncpy(inst, nexttok + 1, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(pos, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(neg, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(ctrlp, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
if (nexttok == NULL) goto baddevice;
|
|
|
|
|
strncpy(ctrln, nexttok, 99); SpiceTokNoNewline();
|
|
|
|
|
|
|
|
|
|
/* make sure all the nodes exist */
|
|
|
|
|
if (LookupObject(pos, CurrentCell) == NULL) Node(pos);
|
|
|
|
|
if (LookupObject(neg, CurrentCell) == NULL) Node(neg);
|
|
|
|
|
if (LookupObject(ctrlp, CurrentCell) == NULL) Node(neg);
|
|
|
|
|
if (LookupObject(ctrln, CurrentCell) == NULL) Node(neg);
|
|
|
|
|
|
|
|
|
|
/* Any device properties? */
|
|
|
|
|
while (nexttok != NULL)
|
|
|
|
|
{
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
|
if ((eqptr = strchr(nexttok, '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*eqptr = '\0';
|
|
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
|
|
|
|
}
|
2016-10-17 23:47:27 +02:00
|
|
|
else if (StringIsValueOrExpression(nexttok)) {
|
2015-05-18 15:27:46 +02:00
|
|
|
AddProperty(&kvlist, "value", nexttok);
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
strcpy(model, "vcvs"); /* Default controlled voltage source */
|
|
|
|
|
if (LookupCellFile(model, filenum) == NULL) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(model, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
Port("pos");
|
|
|
|
|
Port("neg");
|
|
|
|
|
Port("ctrlp");
|
|
|
|
|
Port("ctrln");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(model, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_MODULE);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
}
|
|
|
|
|
else if (CountPorts(model, filenum) != 4) {
|
|
|
|
|
/* Modeled device: Make sure it has the right number of ports */
|
|
|
|
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
|
|
|
|
"controlled voltage source.\n");
|
|
|
|
|
goto baddevice;
|
|
|
|
|
}
|
|
|
|
|
Cell(instname, model, pos, neg, ctrlp, ctrln);
|
|
|
|
|
pobj = LinkProperties(model, kvlist);
|
2016-06-24 04:27:34 +02:00
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
2015-05-18 15:27:46 +02:00
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (toupper(nexttok[0]) == 'X') { /* subcircuit instances */
|
|
|
|
|
char instancename[100], subcktname[100];
|
2018-06-26 03:21:45 +02:00
|
|
|
int itype, in_props;
|
2016-10-18 20:17:57 +02:00
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
instancename[99] = '\0';
|
|
|
|
|
subcktname[99] = '\0';
|
|
|
|
|
|
|
|
|
|
struct portelement {
|
|
|
|
|
char *name;
|
|
|
|
|
struct portelement *next;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct portelement *head, *tail, *scan, *scannext;
|
|
|
|
|
struct objlist *obptr;
|
|
|
|
|
|
|
|
|
|
snprintf(instancename, 99, "%s", nexttok + 1);
|
|
|
|
|
strncpy(instancename, nexttok + 1, 99);
|
|
|
|
|
if (!(*CellStackPtr)) {
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
PushStack(fname, CellStackPtr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
head = NULL;
|
|
|
|
|
tail = NULL;
|
|
|
|
|
SpiceTokNoNewline();
|
2018-06-26 03:21:45 +02:00
|
|
|
in_props = FALSE;
|
2015-05-18 15:27:46 +02:00
|
|
|
while (nexttok != NULL) {
|
|
|
|
|
/* must still be a node or a parameter */
|
|
|
|
|
struct portelement *new_port;
|
|
|
|
|
|
|
|
|
|
// CDL format compatibility: Ignore "/" before the subcircuit name
|
|
|
|
|
if (matchnocase(nexttok, "/")) {
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// And (why do they have to keep messing with a perfectly good syntax?!)
|
|
|
|
|
// prepended to the name without a space:
|
|
|
|
|
else if (*nexttok == '/') nexttok++;
|
|
|
|
|
|
|
|
|
|
// Ignore token called "PARAMS:"
|
|
|
|
|
if (!strcasecmp(nexttok, "PARAMS:")) {
|
|
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We need to look for parameters of the type "name=value" BUT
|
|
|
|
|
// we also need to make sure that what we think is a parameter
|
|
|
|
|
// is actually a circuit name with an equals sign character in it.
|
|
|
|
|
|
|
|
|
|
if (((eqptr = strchr(nexttok, '=')) != NULL) &&
|
|
|
|
|
((tp = LookupCellFile(nexttok, filenum)) == NULL))
|
|
|
|
|
{
|
2018-06-26 03:21:45 +02:00
|
|
|
in_props = TRUE;
|
2015-05-18 15:27:46 +02:00
|
|
|
*eqptr = '\0';
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
AddProperty(&kvlist, nexttok, eqptr + 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
2018-06-26 03:21:45 +02:00
|
|
|
else if (in_props == FALSE)
|
2015-05-18 15:27:46 +02:00
|
|
|
{
|
|
|
|
|
new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement));
|
|
|
|
|
new_port->name = strsave(nexttok);
|
|
|
|
|
if (head == NULL) head = new_port;
|
|
|
|
|
else tail->next = new_port;
|
|
|
|
|
new_port->next = NULL;
|
|
|
|
|
tail = new_port;
|
|
|
|
|
}
|
2018-06-26 03:21:45 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Fprintf(stderr, "Token \"%s\" is not a parameter!\n", nexttok);
|
|
|
|
|
InputParseError(stderr);
|
|
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
SpiceTokNoNewline();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* find the last element of the list, which is not a port,
|
|
|
|
|
but the class type */
|
|
|
|
|
scan = head;
|
|
|
|
|
while (scan != NULL && scan->next != tail && scan->next != NULL)
|
|
|
|
|
scan = scan->next;
|
|
|
|
|
tail = scan;
|
|
|
|
|
if (scan == NULL) goto baddevice;
|
|
|
|
|
if (scan->next != NULL) scan = scan->next;
|
|
|
|
|
tail->next = NULL;
|
|
|
|
|
|
2016-10-18 20:17:57 +02:00
|
|
|
/* Check for ignored class */
|
|
|
|
|
|
|
|
|
|
if ((itype = IsIgnored(subcktname, filenum)) == IGNORE_CLASS) {
|
|
|
|
|
Printf("Class '%s' instanced in input but is being ignored.\n", model);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check for shorted pins */
|
|
|
|
|
|
|
|
|
|
if ((itype == IGNORE_SHORTED) && (head != NULL)) {
|
|
|
|
|
unsigned char shorted = (unsigned char)1;
|
|
|
|
|
struct portelement *p;
|
|
|
|
|
for (p = head->next; p; p = p->next) {
|
|
|
|
|
if (strcasecmp(head->name, p->name))
|
|
|
|
|
shorted = (unsigned char)0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (shorted == (unsigned char)1) {
|
|
|
|
|
Printf("Instance of '%s' is shorted, ignoring.\n", subcktname);
|
|
|
|
|
while (head) {
|
|
|
|
|
p = head->next;
|
|
|
|
|
FREE(head);
|
|
|
|
|
head = p;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
/* Create cell name and revise instance name based on the cell name */
|
|
|
|
|
/* For clarity, if "instancename" does not contain the cellname, */
|
|
|
|
|
/* then prepend the cellname to the instance name. HOWEVER, if any */
|
|
|
|
|
/* netlist is using instancename/portname to name nets, then we */
|
|
|
|
|
/* will have duplicate node names with conflicting records. So at */
|
|
|
|
|
/* very least prepend an "/" to it. . . */
|
|
|
|
|
|
|
|
|
|
/* NOTE: Previously an 'X' was prepended to the name, but this */
|
|
|
|
|
/* caused serious and common errors where, for example, the circuit */
|
|
|
|
|
/* defined cells NOR and XNOR, causing confusion between node */
|
|
|
|
|
/* names. */
|
|
|
|
|
|
|
|
|
|
if (strncmp(instancename, scan->name, strlen(scan->name))) {
|
|
|
|
|
snprintf(subcktname, 99, "%s%s", scan->name, instancename);
|
|
|
|
|
strcpy(instancename, subcktname);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
snprintf(subcktname, 99, "/%s", instancename);
|
|
|
|
|
strcpy(instancename, subcktname);
|
|
|
|
|
}
|
|
|
|
|
snprintf(subcktname, 99, "%s", scan->name);
|
|
|
|
|
|
|
|
|
|
if (scan == head) {
|
|
|
|
|
head = NULL;
|
|
|
|
|
Fprintf(stderr, "Warning: Cell %s has no pins\n", scan->name);
|
|
|
|
|
}
|
|
|
|
|
FREE (scan->name);
|
|
|
|
|
FREE (scan);
|
|
|
|
|
|
|
|
|
|
/* Check that the subcell exists. If not, print a warning and */
|
|
|
|
|
/* generate an empty subcircuit entry matching the call. */
|
|
|
|
|
|
|
|
|
|
tp = LookupCellFile(subcktname, filenum);
|
|
|
|
|
if (tp == NULL) {
|
|
|
|
|
char defport[8];
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
Fprintf(stdout, "Call to undefined subcircuit %s\n"
|
|
|
|
|
"Creating placeholder cell definition.\n", subcktname);
|
2018-04-12 23:09:10 +02:00
|
|
|
CellDef(subcktname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
CurrentCell->flags |= CELL_PLACEHOLDER;
|
|
|
|
|
for (scan = head, i = 1; scan != NULL; scan = scan->next, i++) {
|
|
|
|
|
sprintf(defport, "%d", i);
|
|
|
|
|
Port(defport);
|
|
|
|
|
}
|
|
|
|
|
if (head == NULL) {
|
|
|
|
|
Port((char *)NULL); // Must have something for pin 1
|
|
|
|
|
}
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
PropertyInteger(subcktname, filenum, "M", 0, 1);
|
2015-05-18 15:27:46 +02:00
|
|
|
SetClass(CLASS_MODULE);
|
|
|
|
|
EndCell();
|
|
|
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
|
|
|
update = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nexttok is now NULL, scan->name points to class */
|
2016-10-18 20:17:57 +02:00
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
Instance(subcktname, instancename);
|
|
|
|
|
pobj = LinkProperties(subcktname, kvlist);
|
|
|
|
|
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
|
|
|
|
|
|
|
|
|
|
/* (Diagnostic) */
|
|
|
|
|
/* Fprintf(stderr, "instancing subcell: %s (%s):", subcktname, instancename); */
|
|
|
|
|
/*
|
2015-05-18 15:27:46 +02:00
|
|
|
for (scan = head; scan != NULL; scan = scan->next)
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
Fprintf(stderr," %s", scan->name);
|
2015-05-18 15:27:46 +02:00
|
|
|
Fprintf(stderr,"\n");
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
*/
|
2015-05-18 15:27:46 +02:00
|
|
|
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
obptr = LookupInstance(instancename, CurrentCell);
|
|
|
|
|
if (obptr != NULL) {
|
|
|
|
|
scan = head;
|
|
|
|
|
if (scan != NULL)
|
|
|
|
|
do {
|
|
|
|
|
if (LookupObject(scan->name, CurrentCell) == NULL) Node(scan->name);
|
|
|
|
|
join(scan->name, obptr->name);
|
|
|
|
|
obptr = obptr->next;
|
|
|
|
|
scan = scan->next;
|
|
|
|
|
} while (obptr != NULL && obptr->type > FIRSTPIN && scan != NULL);
|
|
|
|
|
|
|
|
|
|
if ((obptr == NULL && scan != NULL) ||
|
2015-05-18 15:27:46 +02:00
|
|
|
(obptr != NULL && scan == NULL && obptr->type > FIRSTPIN)) {
|
|
|
|
|
if (warnings <= 100) {
|
|
|
|
|
Fprintf(stderr,"Parameter list mismatch in %s: ", instancename);
|
|
|
|
|
|
|
|
|
|
if (obptr == NULL)
|
|
|
|
|
Fprintf(stderr, "Too many parameters in call!\n");
|
|
|
|
|
else if (scan == NULL)
|
|
|
|
|
Fprintf(stderr, "Not enough parameters in call!\n");
|
|
|
|
|
InputParseError(stderr);
|
|
|
|
|
if (warnings == 100)
|
|
|
|
|
Fprintf(stderr, "Too many warnings. . . will not report any more.\n");
|
|
|
|
|
}
|
|
|
|
|
warnings++;
|
Finished basic implementation of matching device properties to
include calculations of effective width due to the addition of
width of multiple device instances in parallel. The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.). Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.
Not yet done: Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
2016-07-11 14:41:07 +02:00
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
DeleteProperties(&kvlist);
|
|
|
|
|
|
|
|
|
|
/* free up the allocated list */
|
|
|
|
|
scan = head;
|
|
|
|
|
while (scan != NULL) {
|
|
|
|
|
scannext = scan->next;
|
|
|
|
|
FREE(scan->name);
|
|
|
|
|
FREE(scan);
|
|
|
|
|
scan = scannext;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (matchnocase(nexttok, ".END")) {
|
|
|
|
|
/* Well, don't take *my* word for it. But we won't flag a warning. */
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
int ntotal;
|
|
|
|
|
char *sstr;
|
|
|
|
|
ntotal = 0;
|
|
|
|
|
for (sstr = nexttok; *sstr != '\0'; sstr++) if (!isascii(*sstr)) ntotal++;
|
|
|
|
|
if ((int)(sstr - nexttok) < (ntotal << 2)) {
|
|
|
|
|
Fprintf(stderr, "Input file \"%s\" appears to be binary"
|
|
|
|
|
". . . bailing out\n", fname);
|
|
|
|
|
while (*CellStackPtr) PopStack(CellStackPtr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (warnings <= 100) {
|
|
|
|
|
Fprintf(stderr, "Ignoring line starting with token: %s\n", nexttok);
|
|
|
|
|
InputParseError(stderr);
|
|
|
|
|
if (warnings == 100)
|
|
|
|
|
Fprintf(stderr, "Too many warnings. . . will not report any more.\n");
|
|
|
|
|
}
|
|
|
|
|
warnings++;
|
|
|
|
|
SpiceSkipNewLine();
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
baddevice:
|
|
|
|
|
Fprintf(stderr, "Badly formed line in input.\n");
|
|
|
|
|
}
|
2015-08-21 14:35:51 +02:00
|
|
|
|
|
|
|
|
/* Watch for bad ending syntax */
|
|
|
|
|
|
2015-08-31 15:33:31 +02:00
|
|
|
if (in_subckt == (char)1) {
|
|
|
|
|
Fprintf(stderr, "Missing .ENDS statement on subcircuit.\n");
|
|
|
|
|
InputParseError(stderr);
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 14:35:51 +02:00
|
|
|
if (*(CellStackPtr)) {
|
|
|
|
|
CleanupSubcell();
|
|
|
|
|
EndCell();
|
|
|
|
|
if (*CellStackPtr) PopStack(CellStackPtr);
|
|
|
|
|
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 15:27:46 +02:00
|
|
|
if (update != 0) RecurseCellFileHashTable(renamepins, filenum);
|
|
|
|
|
|
|
|
|
|
if (warnings)
|
|
|
|
|
Fprintf(stderr, "File %s read with %d warning%s.\n", fname,
|
|
|
|
|
warnings, (warnings == 1) ? "" : "s");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*/
|
|
|
|
|
/* Top-level SPICE file read routine */
|
|
|
|
|
/*----------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
char *ReadSpiceTop(char *fname, int *fnum, int blackbox)
|
|
|
|
|
{
|
|
|
|
|
struct cellstack *CellStack = NULL;
|
|
|
|
|
struct nlist *tp;
|
|
|
|
|
int filenum;
|
|
|
|
|
|
|
|
|
|
// Make sure CurrentCell is clear
|
|
|
|
|
CurrentCell = NULL;
|
|
|
|
|
|
|
|
|
|
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
|
|
|
|
|
|
2020-07-29 19:57:08 +02:00
|
|
|
if (strrchr(fname, '.') == NULL) {
|
|
|
|
|
char name[1024];
|
|
|
|
|
SetExtension(name, fname, SPICE_EXTENSION);
|
|
|
|
|
if ((filenum = OpenParseFile(name, *fnum)) < 0) {
|
|
|
|
|
Fprintf(stderr, "Error in SPICE file read: No file %s\n", name);
|
|
|
|
|
*fnum = filenum;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Fprintf(stderr, "Error in SPICE file read: No file %s\n", fname);
|
2015-05-18 15:27:46 +02:00
|
|
|
*fnum = filenum;
|
|
|
|
|
return NULL;
|
2020-07-29 19:57:08 +02:00
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-31 04:51:34 +02:00
|
|
|
/* Make sure all SPICE file reading is case insensitive */
|
|
|
|
|
/* BUT if a verilog file was read before it, then it will */
|
|
|
|
|
/* be forced to be case sensitive, caveat end-user. */
|
|
|
|
|
|
|
|
|
|
if (matchfunc == match) {
|
|
|
|
|
Printf("Warning: A case-sensitive file has been read and so the "
|
|
|
|
|
"SPICE netlist must be treated case-sensitive to match.\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
matchfunc = matchnocase;
|
|
|
|
|
matchintfunc = matchfilenocase;
|
|
|
|
|
hashfunc = hashnocase;
|
|
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
|
2016-06-23 16:13:18 +02:00
|
|
|
InitializeHashTable(&spiceparams, OBJHASHSIZE);
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
/* All spice files should start with a comment line, */
|
|
|
|
|
/* but we won't depend upon it. Any comment line */
|
|
|
|
|
/* will be handled by the main SPICE file processing. */
|
|
|
|
|
|
|
|
|
|
ReadSpiceFile(fname, filenum, &CellStack, blackbox);
|
|
|
|
|
CloseParseFile();
|
|
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
|
while (CellStack != NULL) PopStack(&CellStack);
|
|
|
|
|
|
2016-06-23 16:13:18 +02:00
|
|
|
RecurseHashTable(&spiceparams, freeprop);
|
|
|
|
|
HashKill(&spiceparams);
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
// Important: If the file is a library, containing subcircuit
|
|
|
|
|
// definitions but no components, then it needs to be registered
|
|
|
|
|
// as an empty cell. Otherwise, the filename is lost and cells
|
|
|
|
|
// cannot be matched to the file!
|
|
|
|
|
|
2018-04-12 23:09:10 +02:00
|
|
|
if (LookupCellFile(fname, filenum) == NULL) CellDef(fname, filenum);
|
2015-05-18 15:27:46 +02:00
|
|
|
|
|
|
|
|
tp = LookupCellFile(fname, filenum);
|
|
|
|
|
if (tp) tp->flags |= CELL_TOP;
|
|
|
|
|
|
|
|
|
|
*fnum = filenum;
|
|
|
|
|
return fname;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
/* Wrappers for ReadSpiceTop() */
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
|
|
|
|
|
char *ReadSpice(char *fname, int *fnum)
|
|
|
|
|
{
|
|
|
|
|
return ReadSpiceTop(fname, fnum, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
|
|
|
|
|
char *ReadSpiceLib(char *fname, int *fnum)
|
|
|
|
|
{
|
|
|
|
|
return ReadSpiceTop(fname, fnum, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
/* SPICE file include routine */
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void IncludeSpice(char *fname, int parent, struct cellstack **CellStackPtr,
|
|
|
|
|
int blackbox)
|
|
|
|
|
{
|
|
|
|
|
int filenum = -1;
|
|
|
|
|
char name[256];
|
|
|
|
|
|
|
|
|
|
/* If fname does not begin with "/", then assume that it is */
|
|
|
|
|
/* in the same relative path as its parent. */
|
|
|
|
|
|
|
|
|
|
if (fname[0] != '/') {
|
|
|
|
|
char *ppath;
|
|
|
|
|
if (*CellStackPtr && ((*CellStackPtr)->cellname != NULL)) {
|
|
|
|
|
strcpy(name, (*CellStackPtr)->cellname);
|
|
|
|
|
ppath = strrchr(name, '/');
|
|
|
|
|
if (ppath != NULL)
|
|
|
|
|
strcpy(ppath + 1, fname);
|
|
|
|
|
else
|
|
|
|
|
strcpy(name, fname);
|
|
|
|
|
filenum = OpenParseFile(name, parent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we failed the path relative to the parent, then try the */
|
|
|
|
|
/* filename alone (relative to the path where netgen was */
|
|
|
|
|
/* executed). */
|
|
|
|
|
|
|
|
|
|
if (filenum < 0) {
|
|
|
|
|
if ((filenum = OpenParseFile(fname, parent)) < 0) {
|
|
|
|
|
|
|
|
|
|
/* If that fails, see if a standard SPICE extension */
|
2017-12-15 03:48:01 +01:00
|
|
|
/* helps, if the file didn't have an extension. But */
|
|
|
|
|
/* really, we're getting desperate at this point. */
|
2015-05-18 15:27:46 +02:00
|
|
|
|
2020-07-25 02:50:07 +02:00
|
|
|
if (strrchr(fname, '.') == NULL) {
|
2017-12-15 03:48:01 +01:00
|
|
|
SetExtension(name, fname, SPICE_EXTENSION);
|
|
|
|
|
filenum = OpenParseFile(name, parent);
|
2020-03-05 15:41:22 +01:00
|
|
|
if (filenum < 0) {
|
|
|
|
|
Fprintf(stderr, "Error in SPICE file include: No file %s\n", name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
2020-03-05 15:41:22 +01:00
|
|
|
else {
|
|
|
|
|
Fprintf(stderr, "Error in SPICE file include: No file %s\n", fname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-18 15:27:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ReadSpiceFile(fname, parent, CellStackPtr, blackbox);
|
|
|
|
|
CloseParseFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void EsacapSubCell(struct nlist *tp, int IsSubCell)
|
|
|
|
|
{
|
|
|
|
|
struct objlist *ob;
|
|
|
|
|
int node, maxnode;
|
|
|
|
|
|
|
|
|
|
/* check to see that all children have been dumped */
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
|
|
|
if (ob->type == FIRSTPIN) {
|
|
|
|
|
struct nlist *tp2;
|
|
|
|
|
|
|
|
|
|
tp2 = LookupCellFile(ob->model.class, tp->file);
|
|
|
|
|
if ((tp2 != NULL) && !(tp2->dumped) && (tp2->class == CLASS_SUBCKT))
|
|
|
|
|
EsacapSubCell(tp2, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print preface, if it is a subcell */
|
|
|
|
|
if (IsSubCell) {
|
|
|
|
|
FlushString("# %s doesn't know how to generate ESACAP subcells\n");
|
|
|
|
|
FlushString("# Look in spice.c \n\n");
|
|
|
|
|
FlushString(".SUBCKT %s ",tp->name);
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
|
|
|
if (IsPortInPortlist(ob, tp)) FlushString("%d ", ob->node);
|
|
|
|
|
FlushString("# End of bogus ESACAP subcell\n");
|
|
|
|
|
FlushString("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print names of all nodes, prefixed by comment character */
|
|
|
|
|
maxnode = 0;
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
|
|
|
if (ob->node > maxnode) maxnode = ob->node;
|
|
|
|
|
|
|
|
|
|
/* was: for (node = 0; node <= maxnode; node++) */
|
|
|
|
|
for (node = 1; node <= maxnode; node++)
|
|
|
|
|
FlushString("# %3d = %s\n", node, NodeName(tp, node));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* traverse list of objects */
|
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
|
|
|
if (ob->type == FIRSTPIN) {
|
|
|
|
|
int drain_node, gate_node, source_node;
|
|
|
|
|
/* print out element, but special-case transistors */
|
|
|
|
|
if (match (ob->model.class, "n") || matchnocase(ob->model.class, "p")) {
|
|
|
|
|
FlushString("X%s ",ob->instance.name);
|
|
|
|
|
/* note: this code is dependent on the order defined in Initialize()*/
|
|
|
|
|
gate_node = ob->node;
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
drain_node = ob->node;
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
source_node = ob->node;
|
|
|
|
|
FlushString("(%d %d %d ",drain_node, gate_node, source_node);
|
|
|
|
|
/* write fake substrate connections: NSUB and PSUB */
|
|
|
|
|
/* write fake transistor sizes: NL, NW, PL and PW */
|
|
|
|
|
/* write fake transistor classes: NCHANNEL and PCHANNEL */
|
|
|
|
|
if (matchnocase(ob->model.class, "n"))
|
|
|
|
|
FlushString("NSUB)=SMOS(TYPE=NCHANNEL,W=NW,L=NL);\n");
|
|
|
|
|
else FlushString("PSUB)=SMOS(TYPE=PCHANNEL,W=PW,L=PL);\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* it must be a subckt */
|
|
|
|
|
FlushString("### BOGUS SUBCKT: X%s %d ", ob->instance.name, ob->node);
|
|
|
|
|
while (ob->next != NULL && ob->next->type > FIRSTPIN) {
|
|
|
|
|
ob = ob->next;
|
|
|
|
|
FlushString("%d ",ob->node);
|
|
|
|
|
}
|
|
|
|
|
FlushString("X%s\n", ob->model.class);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsSubCell) FlushString(".ENDS\n");
|
|
|
|
|
tp->dumped = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
/*--------------------------------------*/
|
|
|
|
|
|
|
|
|
|
void EsacapCell(char *name, char *filename)
|
|
|
|
|
{
|
|
|
|
|
struct nlist *tp;
|
|
|
|
|
char FileName[500];
|
|
|
|
|
|
|
|
|
|
tp = LookupCellFile(name, -1);
|
|
|
|
|
if (tp == NULL) {
|
|
|
|
|
Printf ("No cell '%s' found.\n", name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filename == NULL || strlen(filename) == 0)
|
|
|
|
|
SetExtension(FileName, name, ESACAP_EXTENSION);
|
|
|
|
|
else
|
|
|
|
|
SetExtension(FileName, filename, ESACAP_EXTENSION);
|
|
|
|
|
|
|
|
|
|
if (!OpenFile(FileName, 80)) {
|
|
|
|
|
perror("ext(): Unable to open output file.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ClearDumpedList();
|
|
|
|
|
/* all Esacap decks begin with the following comment line */
|
|
|
|
|
FlushString("# ESACAP deck for cell %s written by Netgen %s.%s\n\n",
|
|
|
|
|
name, NETGEN_VERSION, NETGEN_REVISION);
|
|
|
|
|
EsacapSubCell(tp, 0);
|
|
|
|
|
FlushString("# end of ESACAP deck written by Netgen %s.%s\n\n",
|
|
|
|
|
NETGEN_VERSION, NETGEN_REVISION);
|
|
|
|
|
CloseFile(FileName);
|
|
|
|
|
}
|