From eabb898578f5a530a0eb01cc2147193e60c39475 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Thu, 26 Oct 2023 15:08:20 -0400 Subject: [PATCH] Added support for converting a SPICE file to a SIM file simulatable with IRSIM including recent changes made to support multiple device types using the subcircuit "x" component type. This requires reading in a .prm file, which incidentally can be used with any SPICE file to inform netgen of the specific component type of any model defined as a subcircuit. --- VERSION | 2 +- base/ext.c | 293 ++++++++++++++++++++++++++++++++++++++++++++-- base/netfile.c | 21 ++-- base/netfile.h | 1 + base/netgen.h | 1 + tcltk/tclnetgen.c | 7 +- 6 files changed, 306 insertions(+), 19 deletions(-) diff --git a/VERSION b/VERSION index f509226..774046f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.260 +1.5.261 diff --git a/base/ext.c b/base/ext.c index 5b9a432..6922c68 100644 --- a/base/ext.c +++ b/base/ext.c @@ -31,6 +31,8 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #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; @@ -450,7 +452,8 @@ void simCell(char *name, int filenum) struct nlist *tp, *tp2; struct objlist *ob, *ob2; char FileName[500], simclass; - short i; + char writeLine[1024], paramString[128]; + short i, p, mult; double l, w, v; tp = LookupCellFile(name, filenum); @@ -459,6 +462,11 @@ void simCell(char *name, int filenum) return; } + /* Check procedure disabled because new extensions to .sim format + * allow 'x' records to represent low-level components that are + * modeled as SPICE subcircuits. + */ +#if 0 /* check to see that all children have been dumped */ ob = tp->cell; while (ob != NULL) { @@ -470,6 +478,7 @@ void simCell(char *name, int filenum) } ob = ob->next; } +#endif SetExtension(FileName, name, SIM_EXTENSION); if (!OpenFile(FileName, 0)) { @@ -478,9 +487,9 @@ void simCell(char *name, int filenum) } /* print out header list */ - /* distance units are multiplied by 100 (distances are in um) */ + /* distance units are multiplied by invlambda */ - FlushString("| units: 100 tech: scmos\n"); + 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) { @@ -516,13 +525,17 @@ void simCell(char *name, int filenum) case CLASS_NPN: simclass = 'b'; break; - default: + case CLASS_SUBCKT: + case CLASS_MODULE: simclass = 'x'; break; + default: + simclass = '|'; + break; } if (simclass != 'x') - FlushString("%c", simclass); + FlushString("%c", simclass); switch (tp2->class) { case CLASS_NMOS: case CLASS_NMOS4: @@ -590,6 +603,97 @@ void simCell(char *name, int filenum) FlushString(" %g\n", v); break; + case CLASS_SUBCKT: case CLASS_MODULE: + *writeLine = 'x'; + *(writeLine + 1) = '\0'; + mult = 1; + 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; @@ -626,6 +730,181 @@ int StrIsInt(char *s) 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("gate"); + Port("source"); + Port("drain"); + 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_SUBCKT); + 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("gate"); + Port("source"); + Port("drain"); + 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_SUBCKT); + 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_SUBCKT); + 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_SUBCKT); + 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_SUBCKT); + 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 */ /*-------------------------*/ @@ -644,7 +923,7 @@ char *ReadSim(char *fname, int *fnum) SetExtension(name, fname, SIM_EXTENSION); if (OpenParseFile(name, *fnum) < 0) { - Printf("Error in ext file read: No file %s\n",name); + Printf("Error in sim file read: No file %s\n",name); *fnum = filenum; return NULL; } @@ -847,7 +1126,7 @@ char *ReadSim(char *fname, int *fnum) } else if (match(nexttok, "r")) { /* 2-port resistors */ if (IgnoreRC) { - /* ignore all capacitances */ + /* ignore all resistances */ SkipNewLine(NULL); } else { diff --git a/base/netfile.c b/base/netfile.c index 41bbad5..d9e6262 100644 --- a/base/netfile.c +++ b/base/netfile.c @@ -886,7 +886,7 @@ char *ReadNetlist(char *fname, int *fnum) }; #ifdef mips - struct filetype formats[7]; + struct filetype formats[8]; formats[0].extension = NTK_EXTENSION; formats[0].proc = ReadNtk; @@ -894,14 +894,16 @@ char *ReadNetlist(char *fname, int *fnum) formats[1].proc = ReadExtHier; formats[2].extension = SIM_EXTENSION; formats[2].proc = ReadSim; - formats[3].extension = SPICE_EXTENSION; - formats[3].proc = ReadSpice; - formats[4].extension = NETGEN_EXTENSION; - formats[4].proc = ReadNetgenFile; - formats[5].extension = VERILOG_EXTENSION; - formats[5].proc = ReadVerilogFile; - formats[6].extension = NULL; - formats[6].proc = NULL; + formats[3].extension = PRM_EXTENSION; + formats[3].proc = ReadPrm; + formats[4].extension = SPICE_EXTENSION; + formats[4].proc = ReadSpice; + formats[5].extension = NETGEN_EXTENSION; + formats[5].proc = ReadNetgenFile; + formats[6].extension = VERILOG_EXTENSION; + formats[6].proc = ReadVerilogFile; + formats[7].extension = NULL; + formats[7].proc = NULL; #else /* not mips (i.e. compiler with reasonable initializers) */ @@ -910,6 +912,7 @@ char *ReadNetlist(char *fname, int *fnum) {NTK_EXTENSION, ReadNtk}, {EXT_EXTENSION, ReadExtHier}, {SIM_EXTENSION, ReadSim}, + {PRM_EXTENSION, ReadPrm}, {SPICE_EXTENSION, ReadSpice}, {SPICE_EXT2, ReadSpice}, {SPICE_EXT3, ReadSpice}, diff --git a/base/netfile.h b/base/netfile.h index 924858a..84227ad 100644 --- a/base/netfile.h +++ b/base/netfile.h @@ -7,6 +7,7 @@ #define WOMBAT_EXTENSION ".wom" #define EXT_EXTENSION ".ext" #define SIM_EXTENSION ".sim" +#define PRM_EXTENSION ".prm" #define SPICE_EXTENSION ".spice" #define SPICE_EXT2 ".spc" #define SPICE_EXT3 ".sp" diff --git a/base/netgen.h b/base/netgen.h index 694d951..a70dbe5 100644 --- a/base/netgen.h +++ b/base/netgen.h @@ -191,6 +191,7 @@ extern char *ReadNtk (char *fname, int *fnum); extern char *ReadExtHier(char *fname, int *fnum); extern char *ReadExtFlat(char *fname, int *fnum); extern char *ReadSim(char *fname, int *fnum); +extern char *ReadPrm(char *fname, int *fnum); extern char *ReadSpice(char *fname, int *fnum); extern char *ReadSpiceLib(char *fname, int *fnum); extern char *ReadNetgenFile (char *fname, int *fnum); diff --git a/tcltk/tclnetgen.c b/tcltk/tclnetgen.c index a9e927a..ddca807 100644 --- a/tcltk/tclnetgen.c +++ b/tcltk/tclnetgen.c @@ -707,11 +707,11 @@ _netgen_readnet(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *formats[] = { - "automatic", "ext", "extflat", "sim", "ntk", "spice", + "automatic", "ext", "extflat", "sim", "prm", "ntk", "spice", "verilog", "netgen", "actel", "xilinx", NULL }; enum FormatIdx { - AUTO_IDX, EXT_IDX, EXTFLAT_IDX, SIM_IDX, NTK_IDX, + AUTO_IDX, EXT_IDX, EXTFLAT_IDX, SIM_IDX, PRM_IDX, NTK_IDX, SPICE_IDX, VERILOG_IDX, NETGEN_IDX, ACTEL_IDX, XILINX_IDX }; struct nlist *tc; @@ -807,6 +807,9 @@ _netgen_readnet(ClientData clientData, case SIM_IDX: retstr = ReadSim(savstr, &filenum); break; + case PRM_IDX: + retstr = ReadPrm(savstr, &filenum); + break; case NTK_IDX: retstr = ReadNtk(savstr, &filenum); break;