1241 lines
38 KiB
C
1241 lines
38 KiB
C
/* "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. */
|
|
|
|
/* ext.c -- Input/output routines for Berkeley .ext and .sim formats */
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h> /* for strtod() */
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
|
|
#include "netgen.h"
|
|
#include "objlist.h"
|
|
#include "netfile.h"
|
|
#include "print.h"
|
|
#include "hash.h"
|
|
|
|
static int invlambda = 100; /* Used in sim and prm files */
|
|
|
|
void extCell(char *name, int filenum)
|
|
{
|
|
struct nlist *tp, *tp2;
|
|
struct objlist *ob, *ob2;
|
|
int i;
|
|
char FileName[500];
|
|
|
|
tp = LookupCellFile(name, filenum);
|
|
if (tp == NULL) {
|
|
Printf ("No cell '%s' found.\n", name);
|
|
return;
|
|
}
|
|
|
|
/* eliminate transistors as cells (EXT KLUGE) */
|
|
ob = tp->cell;
|
|
if (match(name, "n") || match(name, "p") || match(name, "e") ||
|
|
match(name, "b") || match(name, "r") || matchnocase(name, "c")) {
|
|
SetExtension(FileName, name, EXT_EXTENSION);
|
|
if (!OpenFile(FileName, 0)) {
|
|
Printf("ext(): Unable to open output file: %s.",FileName);
|
|
return;
|
|
}
|
|
FlushString("timestamp 500000000\n");
|
|
FlushString("version 4.0\n");
|
|
FlushString("tech scmos\n");
|
|
ob2 = ob;
|
|
for (i = 0; i < 3; i++) {
|
|
FlushString("node \"%s\" 1 1 0 0\n", ob2->name);
|
|
ob2 = ob2->next;
|
|
}
|
|
FlushString("fet %sfet 0 0 0 0 0 0 0 ", name);
|
|
ob2 = ob;
|
|
for (i = 0; i < 3; i++) {
|
|
FlushString("\"%s\" 4 0 ", ob2->name);
|
|
ob2 = ob2->next;
|
|
}
|
|
FlushString("\n");
|
|
CloseFile(FileName);
|
|
tp->dumped = 1; /* set dumped flag */
|
|
return;
|
|
}
|
|
|
|
/* check to see that all children have been dumped */
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
if (ob->type == FIRSTPIN && ob->model.class) {
|
|
tp2 = LookupCellFile(ob->model.class, filenum);
|
|
if ((tp2 != NULL) && !(tp2->dumped))
|
|
extCell(tp2->name, filenum);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
|
|
SetExtension(FileName, name, EXT_EXTENSION);
|
|
if (!OpenFile(FileName, 0)) {
|
|
perror("ext(): Unable to open output file.");
|
|
return;
|
|
}
|
|
|
|
/* print out header list */
|
|
FlushString("timestamp 500000000\n");
|
|
FlushString("version 4.0\n");
|
|
FlushString("tech scmos\n");
|
|
|
|
/* run through cell's contents, defining all ports and nodes */
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
if ((ob->type == NODE) || IsPort(ob)) {
|
|
char *nodename;
|
|
|
|
FlushString ("node \"%s\" 1 1 0 0\n", ob->name);
|
|
nodename = NodeAlias(tp,ob);
|
|
if (!match(ob->name, nodename))
|
|
FlushString ("merge \"%s\" \"%s\"\n", ob->name, nodename);
|
|
}
|
|
|
|
/* now run through cell's contents, print instances */
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
/* this is an instance */
|
|
/* print out cell, but special case transistors */
|
|
FlushString ("use %s %s 0 0 0 0 0 0\n",
|
|
ob->model.class, ob->instance.name);
|
|
/* print out parameter list */
|
|
ob2 = ob;
|
|
do {
|
|
char *nodename;
|
|
nodename = NodeAlias(tp, ob2);
|
|
if (!match(ob2->name, nodename))
|
|
FlushString ("merge \"%s\" \"%s\"\n", ob2->name, nodename);
|
|
ob2 = ob2->next;
|
|
} while ((ob2 != NULL) && (ob2->type > FIRSTPIN));
|
|
}
|
|
}
|
|
FlushString ("\n");
|
|
CloseFile(FileName);
|
|
Printf("Wrote file: %s\n",FileName);
|
|
tp->dumped = 1; /* set dumped flag */
|
|
}
|
|
|
|
|
|
void Ext(char *name, int filenum)
|
|
{
|
|
ClearDumpedList();
|
|
if (LookupCellFile(name, filenum) != NULL)
|
|
extCell(name, filenum);
|
|
}
|
|
|
|
void GetExtName(char *name, char *nexttok)
|
|
{
|
|
#ifndef TCL_NETGEN
|
|
char *p;
|
|
#endif
|
|
|
|
/* strip leading and trailing quotes, if any exist */
|
|
if (*nexttok == '"') {
|
|
strcpy(name, nexttok+1);
|
|
name[strlen(name) - 1] = '\0';
|
|
}
|
|
else strcpy(name, nexttok);
|
|
|
|
#ifndef TCL_NETGEN
|
|
/* Quick hack to circumvent problems parsing brackets from magic. */
|
|
/* Tcl version attempts to deal with arrays properly and has a */
|
|
/* command to suppress wildcard behavior. */
|
|
while ((p = strpbrk(name, "[]")) != NULL) *p = '_';
|
|
#endif
|
|
}
|
|
|
|
char *ReadExt(char *fname, int doflat, int *fnum)
|
|
{
|
|
int cdnum = 1, rdnum = 1;
|
|
int CellDefInProgress = 0;
|
|
int filenum;
|
|
|
|
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
|
|
char name[MAX_STR_LEN];
|
|
|
|
SetExtension(name, fname, EXT_EXTENSION);
|
|
if ((filenum = OpenParseFile(name, *fnum)) < 0) {
|
|
Printf("Error in ext file read: No file %s\n",name);
|
|
*fnum = filenum;
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* If "fname" had an extension on it, we don't want */
|
|
/* the extension in the cell name. */
|
|
char *pptr;
|
|
if ((pptr = strrchr(fname, '.')) != NULL) *pptr = '\0';
|
|
}
|
|
|
|
/* Make sure all .ext file reading is case sensitive */
|
|
matchfunc = match;
|
|
matchintfunc = matchfile;
|
|
hashfunc = hashcase;
|
|
|
|
if (LookupCellFile(fname, filenum) != NULL) {
|
|
Printf("Error: Duplicate cell name \"%s\"!\n", fname);
|
|
CloseParseFile();
|
|
*fnum = filenum;
|
|
return NULL;
|
|
}
|
|
|
|
while (!EndParseFile()) {
|
|
SkipTok(NULL);
|
|
|
|
if (EndParseFile()) break;
|
|
if (nexttok[0] == '#') SkipNewLine(NULL);
|
|
else if (match(nexttok, "timestamp")) SkipNewLine(NULL);
|
|
else if (match(nexttok, "version")) SkipNewLine(NULL);
|
|
else if (match(nexttok, "tech")) SkipNewLine(NULL);
|
|
else if (match(nexttok, "scale")) SkipNewLine(NULL);
|
|
else if (match(nexttok, "style")) SkipNewLine(NULL);
|
|
else if (match(nexttok, "resistclasses")) SkipNewLine(NULL);
|
|
else if (match(nexttok, "node")) {
|
|
char name[MAX_STR_LEN];
|
|
|
|
/* No cell is generated until at least one valid "node" or "use" */
|
|
/* has been read in the file. */
|
|
|
|
if (!CellDefInProgress) {
|
|
CellDef(fname, filenum);
|
|
CellDefInProgress = 1;
|
|
}
|
|
SkipTok(NULL);
|
|
GetExtName(name, nexttok);
|
|
Node(name); /* Ports will be determined by context */
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "equiv")) {
|
|
char name[MAX_STR_LEN];
|
|
char name2[MAX_STR_LEN];
|
|
SkipTok(NULL);
|
|
GetExtName(name, nexttok);
|
|
if (LookupObject(name,CurrentCell) == NULL) Node(name);
|
|
SkipTok(NULL);
|
|
GetExtName(name2, nexttok);
|
|
if (LookupObject(name2,CurrentCell) == NULL) Node(name2);
|
|
join(name, name2);
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "device")) {
|
|
char dev_name[MAX_STR_LEN], dev_class[MAX_STR_LEN];
|
|
char gate[MAX_STR_LEN], drain[MAX_STR_LEN], source[MAX_STR_LEN], subs[MAX_STR_LEN];
|
|
char inststr[64];
|
|
SkipTok(NULL);
|
|
strcpy(dev_class, nexttok);
|
|
SkipTok(NULL);
|
|
strcpy(dev_name, nexttok);
|
|
SkipTok(NULL); /* x coord of gate box */
|
|
strcpy(inststr, dev_class);
|
|
strcat(inststr, "@");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL); /* y coord of gate box */
|
|
strcat(inststr, ",");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL); /* skip coord of gate box */
|
|
SkipTok(NULL); /* skip coord of gate box */
|
|
|
|
/* Device-dependent parameters */
|
|
|
|
if (match(dev_class, "mosfet")) {
|
|
SkipTok(NULL); /* skip device length */
|
|
SkipTok(NULL); /* skip device width */
|
|
SkipTok(NULL);
|
|
GetExtName(subs, nexttok);
|
|
}
|
|
else if (match(dev_class, "bjt")) {
|
|
SkipTok(NULL); /* skip device length */
|
|
SkipTok(NULL); /* skip device width */
|
|
SkipTok(NULL);
|
|
GetExtName(subs, nexttok);
|
|
}
|
|
else if (match(dev_class, "devcap")) {
|
|
SkipTok(NULL); /* skip device length */
|
|
SkipTok(NULL); /* skip device width */ /* or. . . */
|
|
}
|
|
else if (match(dev_class, "devres")) {
|
|
SkipTok(NULL); /* skip device length */
|
|
SkipTok(NULL); /* skip device width */ /* or. . . */
|
|
}
|
|
else if (match(dev_class, "diode")) {
|
|
SkipTok(NULL);
|
|
GetExtName(gate, nexttok);
|
|
SkipTok(NULL); /* skip terminal length */
|
|
SkipTok(NULL); /* skip terminal attributes */
|
|
SkipTok(NULL);
|
|
GetExtName(drain, nexttok);
|
|
SkipTok(NULL); /* skip terminal length */
|
|
SkipTok(NULL); /* skip terminal attributes */
|
|
}
|
|
else if (match(dev_class, "subckt")) {
|
|
SkipTok(NULL); /* skip device length */
|
|
SkipTok(NULL); /* skip device width */
|
|
SkipTok(NULL);
|
|
GetExtName(subs, nexttok);
|
|
while (nexttok != NULL) {
|
|
SkipTok(NULL);
|
|
GetExtName(gate, nexttok);
|
|
SkipTok(NULL); /* skip terminal length */
|
|
SkipTok(NULL); /* skip terminal attributes */
|
|
}
|
|
}
|
|
else if (match(dev_class, "rsubckt")) {
|
|
SkipTok(NULL); /* skip device length */
|
|
SkipTok(NULL); /* skip device width */
|
|
SkipTok(NULL);
|
|
GetExtName(subs, nexttok);
|
|
}
|
|
SkipTokNoNewline(NULL);
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "fet")) { /* old-style FET record */
|
|
char fet_class[MAX_STR_LEN];
|
|
char gate[MAX_STR_LEN], drain[MAX_STR_LEN], source[MAX_STR_LEN], subs[MAX_STR_LEN];
|
|
char inststr[64];
|
|
SkipTok(NULL);
|
|
strcpy(fet_class, nexttok);
|
|
SkipTok(NULL); /* x coord of gate box */
|
|
strcpy(inststr, fet_class);
|
|
strcat(inststr, "@");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL); /* y coord of gate box */
|
|
strcat(inststr, ",");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL); /* skip coord of gate box */
|
|
SkipTok(NULL); /* skip coord of gate box */
|
|
SkipTok(NULL); /* skip gate area */
|
|
SkipTok(NULL); /* skip gate perimeter */
|
|
SkipTok(NULL);
|
|
GetExtName(subs, nexttok);
|
|
SkipTok(NULL);
|
|
GetExtName(gate, nexttok);
|
|
SkipTok(NULL); /* skip terminal length */
|
|
SkipTok(NULL); /* skip terminal attributes */
|
|
SkipTok(NULL);
|
|
GetExtName(drain, nexttok);
|
|
SkipTok(NULL); /* skip terminal length */
|
|
SkipTok(NULL); /* skip terminal attributes */
|
|
SkipTokNoNewline(NULL);
|
|
if (nexttok == NULL) {
|
|
/* This gets around a problem with the magic extractor in which */
|
|
/* transistors having shorted source-drain are written into the */
|
|
/* .ext file missing one terminal. This should be corrected in */
|
|
/* magic. */
|
|
strcpy(source, drain);
|
|
}
|
|
else
|
|
GetExtName(source, nexttok);
|
|
SkipNewLine(NULL);
|
|
/* remap transistors into things we know about */
|
|
if (match(fet_class, "nfet"))
|
|
N(fname, inststr, gate, drain, source);
|
|
else if (match(fet_class, "pfet"))
|
|
P(fname, inststr, gate, drain, source);
|
|
else if (match(fet_class, "ecap"))
|
|
E(fname, inststr, gate, drain, source);
|
|
else if (match(fet_class, "bnpn"))
|
|
B(fname, inststr, subs, gate, source);
|
|
else if (match(fet_class, "zpolyResistor"))
|
|
Res3(fname, inststr, gate, drain, source);
|
|
else {
|
|
Printf("Unknown fet type in ext: '%s'\n", fet_class);
|
|
InputParseError(stderr);
|
|
}
|
|
}
|
|
else if (match(nexttok, "cap")) {
|
|
if (IgnoreRC) {
|
|
/* ignore all capacitances */
|
|
SkipNewLine(NULL);
|
|
}
|
|
else {
|
|
char ctop[MAX_STR_LEN], cbot[MAX_STR_LEN], cdummy[MAX_STR_LEN];
|
|
SkipTok(NULL);
|
|
GetExtName(ctop, nexttok);
|
|
SkipTok(NULL);
|
|
GetExtName(cbot, nexttok);
|
|
SkipNewLine(NULL); /* Skip over capacitance value */
|
|
Cap(fname, NULL, ctop, cbot);
|
|
}
|
|
}
|
|
else if (match(nexttok, "use")) {
|
|
char name[MAX_STR_LEN];
|
|
char instancename[MAX_STR_LEN];
|
|
char *basename;
|
|
|
|
/* No cell is generated until at least one valid "node" or "use" */
|
|
/* has been read in the file. */
|
|
|
|
if (!CellDefInProgress) {
|
|
CellDef(fname, filenum);
|
|
CellDefInProgress = 1;
|
|
}
|
|
|
|
SkipTok(NULL);
|
|
GetExtName(name, nexttok);
|
|
if ((basename = strrchr(name,'/')) != NULL) {
|
|
char tmp[MAX_STR_LEN];
|
|
strcpy(tmp, basename+1);
|
|
strcpy(name, tmp);
|
|
}
|
|
SkipTok(NULL);
|
|
GetExtName(instancename, nexttok);
|
|
Printf("Instancing %s as %s\n", name, instancename);
|
|
Instance(name, instancename);
|
|
if (doflat) {
|
|
Printf("Flattening %s in %s\n", instancename, fname);
|
|
flattenInstancesOf(NULL, filenum, name);
|
|
}
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "merge")) {
|
|
char name[MAX_STR_LEN];
|
|
char name2[MAX_STR_LEN];
|
|
SkipTok(NULL);
|
|
GetExtName(name, nexttok);
|
|
SkipTok(NULL);
|
|
GetExtName(name2, nexttok);
|
|
if (doflat)
|
|
join(name, name2);
|
|
else if ((strchr(name, '/') == NULL) && (strchr(name2, '/') == NULL))
|
|
join(name, name2);
|
|
else {
|
|
}
|
|
SkipNewLine(NULL);
|
|
}
|
|
else {
|
|
Printf("Strange token in ext: '%s'\n", nexttok);
|
|
InputParseError(stderr);
|
|
SkipNewLine(NULL);
|
|
}
|
|
}
|
|
CloseParseFile();
|
|
*fnum = filenum;
|
|
return (CellDefInProgress) ? CurrentCell->name : NULL;
|
|
}
|
|
|
|
/* Hierarchical and quasi-Flattened versions of the above */
|
|
|
|
char *ReadExtHier(char *fname, int *fnum)
|
|
{
|
|
return ReadExt(fname, 0, fnum);
|
|
}
|
|
|
|
char *ReadExtFlat(char *fname, int *fnum)
|
|
{
|
|
return ReadExt(fname, 1, fnum);
|
|
}
|
|
|
|
|
|
/*********************** .SIM FORMAT SUPPORT **************************/
|
|
|
|
void simCell(char *name, int filenum)
|
|
{
|
|
struct nlist *tp, *tp2;
|
|
struct objlist *ob, *ob2;
|
|
char FileName[500], simclass;
|
|
char writeLine[1024], paramString[128];
|
|
short i, p, mult;
|
|
double l, w, v;
|
|
|
|
tp = LookupCellFile(name, filenum);
|
|
if (tp == NULL) {
|
|
Printf ("No cell '%s' found.\n", name);
|
|
return;
|
|
}
|
|
|
|
/* check to see that all children have been dumped */
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
if (ob->type == FIRSTPIN && ob->model.class) {
|
|
tp2 = LookupCellFile(ob->model.class, filenum);
|
|
if ((tp2 != NULL) && !(tp2->dumped) && (tp2->class == CLASS_SUBCKT))
|
|
Printf("Cell must be flat before .SIM written. Found instance: %s\n",
|
|
tp2->name);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
|
|
SetExtension(FileName, name, SIM_EXTENSION);
|
|
if (!OpenFile(FileName, 0)) {
|
|
perror("sim(): Unable to open output file.");
|
|
return;
|
|
}
|
|
|
|
/* print out header list */
|
|
/* distance units are multiplied by invlambda */
|
|
|
|
FlushString("| units: %d tech: scmos\n", 100 * invlambda);
|
|
|
|
/* now run through cell's contents, print instances */
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
|
|
/* this is an instance */
|
|
|
|
tp2 = LookupCellFile(ob->model.class, filenum);
|
|
switch (tp2->class) {
|
|
case CLASS_NMOS: case CLASS_NMOS4:
|
|
simclass = 'n';
|
|
break;
|
|
case CLASS_PMOS: case CLASS_PMOS4:
|
|
simclass = 'p';
|
|
break;
|
|
case CLASS_FET4: case CLASS_FET3: case CLASS_FET:
|
|
/* take an educated guess. . . */
|
|
if (tolower(ob->model.class[0]) == 'p')
|
|
simclass = 'p';
|
|
else if (tolower(ob->model.class[0]) == 'n')
|
|
simclass = 'n';
|
|
else if (tolower(ob->model.class[strlen(ob->model.class) - 1]) == 'p')
|
|
simclass = 'p';
|
|
else
|
|
simclass = 'n';
|
|
break;
|
|
case CLASS_CAP:
|
|
simclass = 'c';
|
|
break;
|
|
case CLASS_RES:
|
|
simclass = 'r';
|
|
break;
|
|
case CLASS_NPN:
|
|
simclass = 'b';
|
|
break;
|
|
case CLASS_SUBCKT:
|
|
case CLASS_MODULE:
|
|
simclass = 'x';
|
|
break;
|
|
default:
|
|
simclass = '|';
|
|
break;
|
|
}
|
|
|
|
if (simclass != 'x')
|
|
FlushString("%c", simclass);
|
|
|
|
switch (tp2->class) {
|
|
case CLASS_NMOS: case CLASS_NMOS4:
|
|
case CLASS_PMOS: case CLASS_PMOS4:
|
|
case CLASS_FET4: case CLASS_FET3: case CLASS_FET:
|
|
|
|
ob2 = ob->next;
|
|
/* write gate and drain */
|
|
FlushString(" %s", NodeAlias(tp, ob2));
|
|
FlushString(" %s", NodeAlias(tp, ob));
|
|
ob2 = ob2->next;
|
|
FlushString(" %s", NodeAlias(tp, ob2)); /* write source */
|
|
|
|
/* Skip any bulk node on 4-terminal devices */
|
|
while ((ob2 != NULL) && (ob2->type > FIRSTPIN)) ob2 = ob2->next;
|
|
|
|
/* default minimum L/W transistors (scale?) */
|
|
l = 2;
|
|
w = 4;
|
|
if (ob2 && ob2->type == PROPERTY) {
|
|
struct property *kl;
|
|
struct valuelist *vl;
|
|
kl = (struct property *)HashLookup("length", &(tp2->propdict));
|
|
vl = (struct valuelist *)ob2->instance.name;
|
|
l = 1.0e6 * vl[kl->idx].value.dval; /* m -> um */
|
|
kl = (struct property *)HashLookup("width", &(tp2->propdict));
|
|
w = 1.0e6 * vl[kl->idx].value.dval; /* m -> um */
|
|
}
|
|
FlushString(" %g %g\n", l, w);
|
|
break;
|
|
|
|
case CLASS_NPN: case CLASS_PNP: case CLASS_BJT:
|
|
ob2 = ob->next;
|
|
FlushString(" %s", NodeAlias(tp, ob2)); /* base */
|
|
ob2 = ob2->next;
|
|
/* emitter and collector */
|
|
FlushString(" %s\n", NodeAlias(tp, ob2));
|
|
FlushString(" %s\n", NodeAlias(tp, ob));
|
|
/* skip any other pins (there shouldn't be any. . .) */
|
|
while ((ob2 != NULL) && (ob2->type > FIRSTPIN)) ob2 = ob2->next;
|
|
break;
|
|
|
|
case CLASS_CAP: case CLASS_RES:
|
|
case CLASS_CAP3: case CLASS_RES3: case CLASS_ECAP:
|
|
v = 1;
|
|
ob2 = ob;
|
|
for (i = 0; i < 2; i++) {
|
|
FlushString(" %s", NodeAlias(tp, ob2));
|
|
ob2 = ob2->next;
|
|
if ((ob2 == NULL) || (ob2->type <= FIRSTPIN)) break;
|
|
}
|
|
while ((ob2 != NULL) && (ob2->type > FIRSTPIN))
|
|
ob2 = ob2->next; /* Skip dummy node on 3-terminal devices */
|
|
|
|
if (ob2 && ob2->type == PROPERTY) {
|
|
struct property *kl;
|
|
struct valuelist *vl;
|
|
kl = (struct property *)HashLookup("value", &(tp2->propdict));
|
|
vl = (struct valuelist *)ob2->instance.name;
|
|
if (tp2->class == CLASS_CAP)
|
|
v = 1.0e15 * vl[kl->idx].value.dval; /* F -> fF */
|
|
else if (tp2->class == CLASS_RES)
|
|
v = vl->value.dval; /* Ohms (no conversion) */
|
|
}
|
|
FlushString(" %g\n", v);
|
|
break;
|
|
|
|
case CLASS_MODULE:
|
|
*writeLine = 'x';
|
|
*(writeLine + 1) = '\0';
|
|
mult = 1;
|
|
|
|
/* Important---Need to look up the cell definition; if
|
|
* the first pin is "drain" then it is a FET, and pins
|
|
* get swapped from D-G-S-B to G-S-D-B. Source and drain
|
|
* are treated here as equivalent because the .sim format
|
|
* has no concept of an asymmetric source and drain.
|
|
*/
|
|
ob2 = tp2->cell;
|
|
if ((*matchfunc)(ob2->next->name, "gate"))
|
|
{
|
|
strcat(writeLine, " ");
|
|
strcat(writeLine, NodeAlias(tp, ob->next));
|
|
strcat(writeLine, " ");
|
|
strcat(writeLine, NodeAlias(tp, ob));
|
|
ob2 = ob->next->next;
|
|
}
|
|
else
|
|
ob2 = ob;
|
|
|
|
while (ob2 != NULL) {
|
|
strcat(writeLine, " ");
|
|
strcat(writeLine, NodeAlias(tp, ob2));
|
|
ob2 = ob2->next;
|
|
if ((ob2 == NULL) || (ob2->type <= FIRSTPIN)) break;
|
|
}
|
|
|
|
if (ob2 && ob2->type == PROPERTY) {
|
|
struct valuelist *vl;
|
|
/* Only known parameters are L, W, X, and Y */
|
|
for (p = 0;; p++) {
|
|
vl = (struct valuelist *)(&(ob2->instance.props[p]));
|
|
if (vl->type == PROP_ENDLIST) {
|
|
strcat(writeLine, " l=1");
|
|
break;
|
|
}
|
|
else if ((*matchfunc)(vl->key, "L")) {
|
|
v = vl->value.dval;
|
|
sprintf(paramString, " l=%d", (int)(0.5 + (v * invlambda)));
|
|
strcat(writeLine, paramString);
|
|
break;
|
|
}
|
|
}
|
|
for (p = 0;; p++) {
|
|
vl = (struct valuelist *)(&(ob2->instance.props[p]));
|
|
if (vl->type == PROP_ENDLIST) {
|
|
strcat(writeLine, " w=1");
|
|
break;
|
|
}
|
|
else if ((*matchfunc)(vl->key, "W")) {
|
|
v = vl->value.dval;
|
|
sprintf(paramString, " w=%d", (int)(0.5 + (v * invlambda)));
|
|
strcat(writeLine, paramString);
|
|
break;
|
|
}
|
|
}
|
|
for (p = 0;; p++) {
|
|
vl = (struct valuelist *)(&(ob2->instance.props[p]));
|
|
if (vl->type == PROP_ENDLIST) {
|
|
strcat(writeLine, " x=0");
|
|
break;
|
|
}
|
|
else if ((*matchfunc)(vl->key, "X")) {
|
|
i = vl->value.ival;
|
|
sprintf(paramString, " x=%d", i);
|
|
strcat(writeLine, paramString);
|
|
break;
|
|
}
|
|
}
|
|
for (p = 0;; p++) {
|
|
vl = (struct valuelist *)(&(ob2->instance.props[p]));
|
|
if (vl->type == PROP_ENDLIST) {
|
|
strcat(writeLine, " y=0");
|
|
break;
|
|
}
|
|
else if ((*matchfunc)(vl->key, "Y")) {
|
|
i = vl->value.ival;
|
|
sprintf(paramString, " y=%d", i);
|
|
strcat(writeLine, paramString);
|
|
}
|
|
}
|
|
for (p = 0;; p++) {
|
|
vl = (struct valuelist *)(&(ob2->instance.props[p]));
|
|
if (vl->type == PROP_ENDLIST) {
|
|
break;
|
|
}
|
|
else if ((*matchfunc)(vl->key, "M")) {
|
|
if (vl->type == PROP_INTEGER)
|
|
mult = vl->value.ival;
|
|
else
|
|
mult = vl->value.dval;
|
|
}
|
|
}
|
|
}
|
|
strcat(writeLine, " ");
|
|
strcat(writeLine, tp2->name);
|
|
strcat(writeLine, "\n");
|
|
|
|
/* Multiple instances (M != 1) are written multiple times.
|
|
* NF is ignored in favor of having a single device with
|
|
* the total width.
|
|
*/
|
|
for (i = 0; i < mult; i++)
|
|
FlushString(writeLine);
|
|
break;
|
|
|
|
default:
|
|
FlushString("| unhandled component %s\n", tp2->name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FlushString ("\n");
|
|
CloseFile(FileName);
|
|
Printf("Wrote file: %s\n",FileName);
|
|
|
|
tp->dumped = 1; /* set dumped flag */
|
|
}
|
|
|
|
|
|
void Sim(char *name, int filenum)
|
|
{
|
|
ClearDumpedList();
|
|
if (LookupCellFile(name, filenum) != NULL)
|
|
simCell(name, filenum);
|
|
}
|
|
|
|
/*-------------------------------------------------*/
|
|
/* Check whether a string token is a valid integer */
|
|
/*-------------------------------------------------*/
|
|
|
|
int StrIsInt(char *s)
|
|
{
|
|
if (*s == '-' || *s == '+') s++;
|
|
while (*s)
|
|
if (!isdigit(*s++))
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*------------------------------------------------------*/
|
|
/* Read a .prm format file. This is specifically to */
|
|
/* get the "device" lines that indicate how a SPICE */
|
|
/* subcircuit model or a .sim "x" record needs to be */
|
|
/* translated into a specific component type like a */
|
|
/* FET, diode, resistor, etc. */
|
|
/*------------------------------------------------------*/
|
|
|
|
char *ReadPrm(char *fname, int *fnum)
|
|
{
|
|
int filenum;
|
|
struct keyvalue *kvlist = NULL;
|
|
struct nlist *tp;
|
|
|
|
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
|
|
char name[MAX_STR_LEN];
|
|
|
|
SetExtension(name, fname, PRM_EXTENSION);
|
|
if (OpenParseFile(name, *fnum) < 0) {
|
|
Printf("Error in prm file read: No file %s\n",name);
|
|
*fnum = filenum;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Make sure all .prm file reading is case INsensitive */
|
|
/* This is because the only reason to read a PRM file */
|
|
/* is to find all the subcircuit device types so that */
|
|
/* a SPICE file can be read and a SIM file written. */
|
|
|
|
matchfunc = matchnocase;
|
|
matchintfunc = matchfile;
|
|
hashfunc = hashnocase;
|
|
|
|
CellDef(fname, filenum);
|
|
|
|
while (!EndParseFile()) {
|
|
char devicename[MAX_STR_LEN];
|
|
SkipTok(NULL);
|
|
|
|
if (EndParseFile()) break;
|
|
if (nexttok[0] == ';') continue; /* Comment line */
|
|
else if (nexttok[0] == '\0') continue; /* Blank line */
|
|
else if (match(nexttok, "lambda")) {
|
|
SkipTok(NULL);
|
|
invlambda = (int)(0.5 + (1.0 / atof(nexttok)));
|
|
SkipNewLine(NULL); /* skip any attributes */
|
|
}
|
|
else if (match(nexttok, "device")) {
|
|
SkipTok(NULL);
|
|
if (match(nexttok, "nfet")) {
|
|
SkipTok(NULL);
|
|
strcpy(devicename, nexttok);
|
|
/* Create 4-terminal nfet subcircuit device record */
|
|
if (LookupCellFile(devicename, filenum) == NULL) {
|
|
CellDef(devicename, filenum);
|
|
Port("drain");
|
|
Port("gate");
|
|
Port("source");
|
|
Port("bulk");
|
|
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
|
|
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
|
|
PropertyInteger(devicename, filenum, "nf", 0, 1);
|
|
PropertyInteger(devicename, filenum, "m", 0, 1);
|
|
SetClass(CLASS_MODULE);
|
|
EndCell();
|
|
ReopenCellDef(fname, filenum);
|
|
}
|
|
LinkProperties(devicename, kvlist);
|
|
SkipNewLine(NULL); /* skip any attributes */
|
|
}
|
|
else if (match(nexttok, "pfet")) {
|
|
SkipTok(NULL);
|
|
strcpy(devicename, nexttok);
|
|
/* Create 4-terminal pfet subcircuit device record */
|
|
if (LookupCellFile(devicename, filenum) == NULL) {
|
|
CellDef(devicename, filenum);
|
|
Port("drain");
|
|
Port("gate");
|
|
Port("source");
|
|
Port("well");
|
|
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
|
|
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
|
|
PropertyInteger(devicename, filenum, "nf", 0, 1);
|
|
PropertyInteger(devicename, filenum, "m", 0, 1);
|
|
SetClass(CLASS_MODULE);
|
|
EndCell();
|
|
ReopenCellDef(fname, filenum);
|
|
}
|
|
LinkProperties(devicename, kvlist);
|
|
SkipNewLine(NULL); /* skip various attributes */
|
|
}
|
|
else if (match(nexttok, "resistor")) {
|
|
SkipTok(NULL);
|
|
strcpy(devicename, nexttok);
|
|
/* Resistor device has additional record for the value */
|
|
/* (Need to do something with this. . . ?) */
|
|
SkipTok(NULL);
|
|
/* Create resistor subcircuit device record */
|
|
if (LookupCellFile(devicename, filenum) == NULL) {
|
|
CellDef(devicename, filenum);
|
|
Port("end_a");
|
|
Port("end_b");
|
|
PropertyDouble(devicename, filenum, "value", 0.01, 0.0);
|
|
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
|
|
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
|
|
PropertyInteger(devicename, filenum, "m", 0, 1);
|
|
SetClass(CLASS_MODULE);
|
|
EndCell();
|
|
ReopenCellDef(fname, filenum);
|
|
}
|
|
LinkProperties(devicename, kvlist);
|
|
SkipNewLine(NULL); /* skip various attributes */
|
|
}
|
|
else if (match(nexttok, "capacitor")) {
|
|
SkipTok(NULL);
|
|
strcpy(devicename, nexttok);
|
|
/* Capacitor device has additional record for the value */
|
|
/* (Need to do something with this. . . ?) */
|
|
SkipTok(NULL);
|
|
/* Create capacitor subcircuit device record */
|
|
if (LookupCellFile(devicename, filenum) == NULL) {
|
|
CellDef(devicename, filenum);
|
|
Port("top");
|
|
Port("bottom");
|
|
PropertyDouble(devicename, filenum, "value", 0.01, 0.0);
|
|
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
|
|
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
|
|
PropertyInteger(devicename, filenum, "m", 0, 1);
|
|
SetClass(CLASS_MODULE);
|
|
EndCell();
|
|
ReopenCellDef(fname, filenum); /* Reopen */
|
|
}
|
|
LinkProperties(devicename, kvlist);
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "diode")) {
|
|
SkipTok(NULL);
|
|
strcpy(devicename, nexttok);
|
|
/* Create diode subcircuit device record */
|
|
if (LookupCellFile(devicename, filenum) == NULL) {
|
|
CellDef(devicename, filenum);
|
|
Port("anode");
|
|
Port("cathode");
|
|
PropertyInteger(devicename, filenum, "m", 0, 1);
|
|
SetClass(CLASS_MODULE);
|
|
EndCell();
|
|
ReopenCellDef(fname, filenum); /* Reopen */
|
|
}
|
|
LinkProperties(devicename, kvlist);
|
|
SkipNewLine(NULL);
|
|
}
|
|
else {
|
|
Printf("Unknown device type in .prm: '%s'\n", nexttok);
|
|
InputParseError(stderr);
|
|
SkipNewLine(NULL);
|
|
}
|
|
}
|
|
else {
|
|
/* Could spell out all the keywords used in .prm files */
|
|
/* but probably not worth the effort. */
|
|
SkipNewLine(NULL);
|
|
}
|
|
DeleteProperties(&kvlist);
|
|
}
|
|
EndCell();
|
|
CloseParseFile();
|
|
|
|
tp = LookupCellFile(fname, filenum);
|
|
if (tp) tp->flags |= CELL_TOP;
|
|
|
|
*fnum = filenum;
|
|
return fname;
|
|
}
|
|
|
|
/*-------------------------*/
|
|
/* Read a .sim format file */
|
|
/*-------------------------*/
|
|
|
|
char *ReadSim(char *fname, int *fnum)
|
|
{
|
|
int cdnum = 1, rdnum = 1, filenum;
|
|
int has_lumped = 0;
|
|
char *vstr;
|
|
struct keyvalue *kvlist = NULL;
|
|
struct nlist *tp;
|
|
double simscale = 1.0;
|
|
|
|
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
|
|
char name[MAX_STR_LEN];
|
|
|
|
SetExtension(name, fname, SIM_EXTENSION);
|
|
if (OpenParseFile(name, *fnum) < 0) {
|
|
Printf("Error in sim file read: No file %s\n",name);
|
|
*fnum = filenum;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Make sure all .sim file reading is case sensitive */
|
|
matchfunc = match;
|
|
matchintfunc = matchfile;
|
|
hashfunc = hashcase;
|
|
|
|
CellDef(fname, filenum);
|
|
|
|
while (!EndParseFile()) {
|
|
SkipTok(NULL);
|
|
|
|
if (EndParseFile()) break;
|
|
if (nexttok[0] == '|') {
|
|
SkipTok(NULL); /* "units" */
|
|
if (!strcmp(nexttok, "units:")) {
|
|
SkipTok(NULL);
|
|
simscale = strtod(nexttok, NULL);
|
|
}
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "n")) {
|
|
char gate[MAX_STR_LEN], drain[MAX_STR_LEN], source[MAX_STR_LEN];
|
|
char inststr[25], *instptr = NULL;
|
|
|
|
SkipTok(NULL);
|
|
GetExtName(gate, nexttok);
|
|
if (LookupObject(gate, CurrentCell) == NULL)
|
|
Node(gate); /* define the node if it does not already exist */
|
|
|
|
SkipTok(NULL);
|
|
GetExtName(drain, nexttok);
|
|
if (LookupObject(drain, CurrentCell) == NULL)
|
|
Node(drain); /* define the node if it does not already exist */
|
|
|
|
SkipTok(NULL);
|
|
GetExtName(source, nexttok);
|
|
if (LookupObject(source, CurrentCell) == NULL)
|
|
Node(source); /* define the node if it does not already exist */
|
|
|
|
SkipTokNoNewline(NULL); /* length */
|
|
if ((nexttok != NULL) && (nexttok[0] != '\0')) {
|
|
vstr = ScaleStringFloatValue(&nexttok[0], simscale * 1e-8);
|
|
AddProperty(&kvlist, "length", vstr);
|
|
SkipTok(NULL); /* width */
|
|
if ((nexttok != NULL) && (nexttok[0] != '\0')) {
|
|
vstr = ScaleStringFloatValue(&nexttok[0], simscale * 1e-8);
|
|
AddProperty(&kvlist, "width", vstr);
|
|
}
|
|
SkipTokNoNewline(NULL);
|
|
if (nexttok != NULL) {
|
|
if (StrIsInt(nexttok)) {
|
|
strcpy(inststr, "n@");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL);
|
|
strcat(inststr, ",");
|
|
strcat(inststr, nexttok);
|
|
instptr = inststr;
|
|
}
|
|
}
|
|
}
|
|
SkipNewLine(NULL); /* skip any attributes */
|
|
N(fname, instptr, gate, drain, source);
|
|
LinkProperties("n", kvlist);
|
|
}
|
|
else if (match(nexttok, "p")) {
|
|
char gate[MAX_STR_LEN], drain[MAX_STR_LEN], source[MAX_STR_LEN];
|
|
char inststr[25], *instptr = NULL;
|
|
SkipTok(NULL);
|
|
GetExtName(gate, nexttok);
|
|
if (LookupObject(gate, CurrentCell) == NULL)
|
|
Node(gate); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(drain, nexttok);
|
|
if (LookupObject(drain, CurrentCell) == NULL)
|
|
Node(drain); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(source, nexttok);
|
|
if (LookupObject(source, CurrentCell) == NULL)
|
|
Node(source); /* define the node if it does not already exist */
|
|
SkipTokNoNewline(NULL); /* length */
|
|
if ((nexttok != NULL) && (nexttok[0] != '\0')) {
|
|
vstr = ScaleStringFloatValue(&nexttok[0], simscale * 1e-8);
|
|
AddProperty(&kvlist, "length", vstr);
|
|
SkipTok(NULL); /* width */
|
|
if ((nexttok != NULL) && (nexttok[0] != '\0')) {
|
|
vstr = ScaleStringFloatValue(&nexttok[0], simscale * 1e-8);
|
|
AddProperty(&kvlist, "width", vstr);
|
|
}
|
|
SkipTokNoNewline(NULL);
|
|
if (nexttok != NULL) {
|
|
if (StrIsInt(nexttok)) {
|
|
strcpy(inststr, "p@");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL);
|
|
strcat(inststr, ",");
|
|
strcat(inststr, nexttok);
|
|
instptr = inststr;
|
|
}
|
|
}
|
|
}
|
|
SkipNewLine(NULL); /* skip various attributes */
|
|
P(fname, instptr, gate, drain, source);
|
|
LinkProperties("p", kvlist);
|
|
}
|
|
else if (match(nexttok, "e")) { /* 3-port capacitors (poly/poly2) */
|
|
char gate[MAX_STR_LEN], drain[MAX_STR_LEN], source[MAX_STR_LEN];
|
|
char inststr[25], *instptr = NULL;
|
|
SkipTok(NULL);
|
|
GetExtName(gate, nexttok);
|
|
if (LookupObject(gate, CurrentCell) == NULL)
|
|
Node(gate); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(drain, nexttok);
|
|
if (LookupObject(drain, CurrentCell) == NULL)
|
|
Node(drain); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(source, nexttok);
|
|
if (LookupObject(source, CurrentCell) == NULL)
|
|
Node(source); /* define the node if it does not already exist */
|
|
SkipTokNoNewline(NULL); /* skip length */
|
|
if (nexttok != NULL) {
|
|
SkipTok(NULL); /* skip width */
|
|
SkipTokNoNewline(NULL);
|
|
inststr[0] = '\0';
|
|
if (nexttok != NULL) {
|
|
if (StrIsInt(nexttok)) {
|
|
strcpy(inststr, "e@");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL);
|
|
strcat(inststr, ",");
|
|
strcat(inststr, nexttok);
|
|
instptr = inststr;
|
|
}
|
|
}
|
|
}
|
|
SkipNewLine(NULL); /* skip various attributes */
|
|
E(fname, instptr, gate, drain, source);
|
|
}
|
|
else if (match(nexttok, "b")) { /* bipolars added by Tim 7/16/96 */
|
|
char base[MAX_STR_LEN], emitter[MAX_STR_LEN], collector[MAX_STR_LEN];
|
|
char inststr[25], *instptr = NULL;
|
|
SkipTok(NULL);
|
|
GetExtName(base, nexttok);
|
|
if (LookupObject(base, CurrentCell) == NULL)
|
|
Node(base); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(emitter, nexttok);
|
|
if (LookupObject(emitter, CurrentCell) == NULL)
|
|
Node(emitter); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(collector, nexttok);
|
|
if (LookupObject(collector, CurrentCell) == NULL)
|
|
Node(collector); /* define the node if it does not already exist */
|
|
SkipTokNoNewline(NULL); /* skip length */
|
|
if (nexttok != NULL) {
|
|
SkipTok(NULL); /* skip width */
|
|
SkipTokNoNewline(NULL);
|
|
if (nexttok != NULL) {
|
|
if (StrIsInt(nexttok)) {
|
|
strcpy(inststr, "b@");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL);
|
|
strcat(inststr, ",");
|
|
strcat(inststr, nexttok);
|
|
instptr = inststr;
|
|
}
|
|
}
|
|
}
|
|
SkipNewLine(NULL); /* skip various attributes */
|
|
B(fname, instptr, collector, base, emitter);
|
|
}
|
|
else if (matchnocase(nexttok, "c")) { /* 2-port capacitors */
|
|
if (IgnoreRC) {
|
|
/* ignore all capacitances */
|
|
SkipNewLine(NULL);
|
|
}
|
|
else {
|
|
char ctop[MAX_STR_LEN], cbot[MAX_STR_LEN], cdummy[MAX_STR_LEN];
|
|
SkipTok(NULL);
|
|
GetExtName(ctop, nexttok);
|
|
if (LookupObject(ctop, CurrentCell) == NULL)
|
|
Node(ctop); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(cbot, nexttok);
|
|
if (LookupObject(cbot, CurrentCell) == NULL)
|
|
Node(cbot); /* define the node if it does not already exist */
|
|
SkipTokNoNewline(NULL);
|
|
if (nexttok != NULL) {
|
|
vstr = ScaleStringFloatValue(&nexttok[0], 1e-15);
|
|
AddProperty(&kvlist, "value", vstr);
|
|
}
|
|
SkipNewLine(NULL);
|
|
Cap(fname, NULL, ctop, cbot);
|
|
LinkProperties("c", kvlist);
|
|
}
|
|
}
|
|
else if (match(nexttok, "r")) { /* 2-port resistors */
|
|
if (IgnoreRC) {
|
|
/* ignore all resistances */
|
|
SkipNewLine(NULL);
|
|
}
|
|
else {
|
|
char rtop[MAX_STR_LEN], rbot[MAX_STR_LEN];
|
|
SkipTok(NULL);
|
|
GetExtName(rtop, nexttok);
|
|
if (LookupObject(rtop, CurrentCell) == NULL)
|
|
Node(rtop); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(rbot, nexttok);
|
|
if (LookupObject(rbot, CurrentCell) == NULL)
|
|
Node(rbot); /* define the node if it does not already exist */
|
|
SkipTokNoNewline(NULL);
|
|
if (nexttok != NULL) {
|
|
AddProperty(&kvlist, "value", &nexttok[0]);
|
|
}
|
|
SkipNewLine(NULL); /* skip various attributes */
|
|
Res(fname, NULL, rtop, rbot);
|
|
LinkProperties("r", kvlist);
|
|
}
|
|
}
|
|
else if (match(nexttok, "z")) { /* 3-port resistors from magic */
|
|
if (IgnoreRC) {
|
|
/* ignore all capacitances */
|
|
SkipNewLine(NULL);
|
|
}
|
|
else {
|
|
char rtop[MAX_STR_LEN], rbot[MAX_STR_LEN], rdummy[MAX_STR_LEN];
|
|
char inststr[25], *instptr = NULL;
|
|
SkipTok(NULL);
|
|
GetExtName(rdummy, nexttok);
|
|
if (LookupObject(rdummy, CurrentCell) == NULL)
|
|
Node(rdummy); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(rtop, nexttok);
|
|
if (LookupObject(rtop, CurrentCell) == NULL)
|
|
Node(rtop); /* define the node if it does not already exist */
|
|
SkipTok(NULL);
|
|
GetExtName(rbot, nexttok);
|
|
if (LookupObject(rbot, CurrentCell) == NULL)
|
|
Node(rbot); /* define the node if it does not already exist */
|
|
SkipTokNoNewline(NULL); /* skip length */
|
|
if (nexttok != NULL) {
|
|
SkipTok(NULL); /* skip width */
|
|
SkipTokNoNewline(NULL);
|
|
if (nexttok != NULL) {
|
|
if (StrIsInt(nexttok)) {
|
|
strcpy(inststr, "z@");
|
|
strcat(inststr, nexttok);
|
|
SkipTok(NULL);
|
|
strcat(inststr, ",");
|
|
strcat(inststr, nexttok);
|
|
instptr = inststr;
|
|
}
|
|
}
|
|
}
|
|
SkipNewLine(NULL); /* skip various attributes */
|
|
Res3(fname, instptr, rdummy, rtop, rbot);
|
|
}
|
|
}
|
|
else if (match(nexttok, "N")) {
|
|
/* ignore this keyword */
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "A")) {
|
|
/* ignore this keyword */
|
|
SkipNewLine(NULL);
|
|
}
|
|
else if (match(nexttok, "=")) {
|
|
char node1[MAX_STR_LEN], node2[MAX_STR_LEN];
|
|
SkipTok(NULL);
|
|
GetExtName(node1, nexttok);
|
|
SkipTok(NULL);
|
|
GetExtName(node2, nexttok);
|
|
join(node1, node2);
|
|
}
|
|
else if (match(nexttok, "R")) {
|
|
if (has_lumped == 0) {
|
|
Printf("Ignoring lumped resistances (\"R\" records) in .sim.\n");
|
|
has_lumped = 1; /* Don't print this message more than once */
|
|
}
|
|
SkipNewLine(NULL);
|
|
}
|
|
else {
|
|
Printf("Strange token in .sim: '%s'\n", nexttok);
|
|
InputParseError(stderr);
|
|
SkipNewLine(NULL);
|
|
}
|
|
DeleteProperties(&kvlist);
|
|
}
|
|
EndCell();
|
|
CloseParseFile();
|
|
|
|
tp = LookupCellFile(fname, filenum);
|
|
if (tp) tp->flags |= CELL_TOP;
|
|
|
|
*fnum = filenum;
|
|
return fname;
|
|
}
|