mirror of
https://github.com/RTimothyEdwards/netgen.git
synced 2026-08-22 14:07:07 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ca77300ac | ||
|
|
6d2ef396ef | ||
|
|
2483b7440f | ||
|
|
236fba18aa | ||
|
|
49c0de0433 | ||
|
|
3b9dca0cf2 | ||
|
|
1272ed22fe | ||
|
|
7d910b616c | ||
|
|
6179ba8cb8 | ||
|
|
b1032f846b | ||
|
|
4c546d1472 | ||
|
|
aaf8fefc1a | ||
|
|
e1aa231db1 | ||
|
|
abaf896f7f | ||
|
|
df8fa29b2f | ||
|
|
e94d25b3f1 | ||
|
|
d14bf70f1c | ||
|
|
e659495ef5 | ||
|
|
5c21000a8b | ||
|
|
2ce3cf8dd9 | ||
|
|
05872ca918 | ||
|
|
05f433f334 | ||
|
|
e821381900 | ||
|
|
2129073a38 | ||
|
|
8022e1370f | ||
|
|
b0d980bb7d | ||
|
|
2b88d79adc | ||
|
|
ab0165b16c | ||
|
|
bf4112db07 | ||
|
|
5197eb6186 | ||
|
|
fcee934580 | ||
|
|
48ed1f7583 | ||
|
|
2d427aef3c | ||
|
|
e63593c7e2 | ||
|
|
fd0c8c87ea | ||
|
|
3d180f778d | ||
|
|
178af5f493 | ||
|
|
035fef5c72 | ||
|
|
ac8956c32e | ||
|
|
202ea0431f | ||
|
|
94754dbc4e | ||
|
|
b87f0fd5db | ||
|
|
bf67d3c275 | ||
|
|
21e9207924 | ||
|
|
62feed812e | ||
|
|
8392721885 | ||
|
|
d1c2848e4b | ||
|
|
6b0bd4d97b | ||
|
|
79bab50a79 | ||
|
|
d69fbc23bb | ||
|
|
b83800d69b | ||
|
|
c7fa0324d9 | ||
|
|
93b9cf6577 | ||
|
|
eb27a18ae3 | ||
|
|
bf53d52970 | ||
|
|
1817f4dd6a | ||
|
|
4250525e19 | ||
|
|
a7e859fcde | ||
|
|
c1ed4ce49e | ||
|
|
6d23844483 | ||
|
|
cc84364263 | ||
|
|
00b906c109 | ||
|
|
ab327c5f82 | ||
|
|
25a0e12428 | ||
|
|
eabb898578 | ||
|
|
2cb3937ee3 | ||
|
|
ec0e097fcf |
+298
-7
@@ -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);
|
||||
@@ -478,9 +481,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 +519,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 +597,115 @@ void simCell(char *name, int filenum)
|
||||
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;
|
||||
@@ -626,6 +742,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("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 */
|
||||
/*-------------------------*/
|
||||
@@ -644,7 +935,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 +1138,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 {
|
||||
|
||||
+59
-18
@@ -136,17 +136,16 @@ void flattenCell(char *name, int file)
|
||||
ob2 = ob2->next;
|
||||
}
|
||||
|
||||
|
||||
/* delete all port elements from child */
|
||||
while (IsPort(ChildObjList)) {
|
||||
/* delete all ports at beginning of list */
|
||||
if (Debug) Printf("deleting leading port from child\n");
|
||||
tmp = ChildObjList->next;
|
||||
FreeObjectAndHash(ChildObjList, ChildCell);
|
||||
ChildObjList = tmp;
|
||||
if ((ChildObjList = tmp) == NULL) break;
|
||||
}
|
||||
tmp = ChildObjList;
|
||||
while (tmp->next != NULL) {
|
||||
while (tmp && (tmp->next != NULL)) {
|
||||
if (IsPort(tmp->next)) {
|
||||
ob2 = (tmp->next)->next;
|
||||
if (Debug) Printf("deleting a port from child\n");
|
||||
@@ -601,8 +600,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||
else break;
|
||||
|
||||
/* Put the child cell at the start of ChildObjList */
|
||||
ChildEnd->next = ChildObjList;
|
||||
ChildObjList = ChildStart;
|
||||
if (ChildEnd) {
|
||||
ChildEnd->next = ChildObjList;
|
||||
ChildObjList = ChildStart;
|
||||
}
|
||||
}
|
||||
|
||||
/* Put the child cell at the start of ChildObjList */
|
||||
@@ -1231,6 +1232,10 @@ int UniquePins(char *name, int filenum)
|
||||
* Removed the code 9/1/2023. But---Not sure if any code depends
|
||||
* on shorted pins being adjacent.
|
||||
*/
|
||||
/* When two pins are shorted, they are by definition permutable */
|
||||
PermuteSetup(ThisCell->name, ThisCell->file, ob->name,
|
||||
firstport[ob->node]->name);
|
||||
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
@@ -1631,7 +1636,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
ECompare *ecomp, *ncomp;
|
||||
ECompList *list0X, *listX0;
|
||||
int hascontents1, hascontents2;
|
||||
int match, modified = 0;
|
||||
int match, modified1 = 0, modified2 = 0;
|
||||
int not_top;
|
||||
|
||||
if (file1 == -1)
|
||||
tc1 = LookupCell(name1);
|
||||
@@ -1708,7 +1714,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
" makes a better match\n", ecomp->cell1->name,
|
||||
name1, file1);
|
||||
flattenInstancesOf(name1, file1, ecomp->cell1->name);
|
||||
modified++;
|
||||
modified1++;
|
||||
}
|
||||
if (ecomp->cell2 && (ecomp->num2 > 0) &&
|
||||
(!(ecomp->cell2->flags & CELL_PLACEHOLDER))) {
|
||||
@@ -1716,7 +1722,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
" makes a better match\n", ecomp->cell2->name,
|
||||
name2, file2);
|
||||
flattenInstancesOf(name2, file2, ecomp->cell2->name);
|
||||
modified++;
|
||||
modified2++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1806,7 +1812,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
" makes a better match\n", ecomp->cell2->name,
|
||||
name2, file2);
|
||||
flattenInstancesOf(name2, file2, ecomp->cell2->name);
|
||||
modified++;
|
||||
modified2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1872,7 +1878,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
" makes a better match\n", ecomp->cell1->name,
|
||||
name1, file1);
|
||||
flattenInstancesOf(name1, file1, ecomp->cell1->name);
|
||||
modified++;
|
||||
modified1++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1902,6 +1908,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
// Remove non-matching zero-value devices. This can
|
||||
// be done on a per-instance basis.
|
||||
|
||||
not_top = (PeekCompareQueueTop(NULL, NULL, NULL, NULL) == -1) ? FALSE : TRUE;
|
||||
|
||||
ecomp = (ECompare *)HashFirst(&compdict);
|
||||
while (ecomp != NULL) {
|
||||
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
|
||||
@@ -1958,7 +1966,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
/* because it will just show up as a */
|
||||
/* port mismatch error as it should. */
|
||||
|
||||
if (!(tc1->flags & CELL_TOP) &&
|
||||
if ((not_top == TRUE) &&
|
||||
(ecomp->cell1->class != CLASS_ISOURCE)) {
|
||||
int found1 = FALSE;
|
||||
int found2 = FALSE;
|
||||
@@ -2024,7 +2032,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
|
||||
/* Remove from list */
|
||||
ecomp->num1--;
|
||||
modified++;
|
||||
modified1++;
|
||||
|
||||
ob1 = lob;
|
||||
}
|
||||
@@ -2096,6 +2104,27 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* (See comments above about removing shorts */
|
||||
/* between two ports.) */
|
||||
|
||||
if ((not_top == TRUE) &&
|
||||
(ecomp->cell2->class != CLASS_ISOURCE)) {
|
||||
int found1 = FALSE;
|
||||
int found2 = FALSE;
|
||||
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
|
||||
if (!IsPort(ob1)) break;
|
||||
else if (ob1->node == node1)
|
||||
found1 = TRUE;
|
||||
else if (ob1->node == node2)
|
||||
found2 = TRUE;
|
||||
if (found1 && found2) {
|
||||
found = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) break;
|
||||
}
|
||||
if (found) {
|
||||
@@ -2141,8 +2170,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
}
|
||||
|
||||
/* Remove from list */
|
||||
ecomp->num1--;
|
||||
modified++;
|
||||
ecomp->num2--;
|
||||
modified2++;
|
||||
|
||||
ob2 = lob;
|
||||
}
|
||||
@@ -2176,7 +2205,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
// are no other modifications, as this rule is relaxed compared to other
|
||||
// rules, and the other rules should be exhaustively applied first.
|
||||
|
||||
if ((listX0 != NULL) && (list0X != NULL) && (modified == 0)) {
|
||||
if ((listX0 != NULL) && (list0X != NULL) && ((modified1 + modified2) == 0)) {
|
||||
ECompare *ecomp0X, *ecompX0;
|
||||
ECompList *elist0X, *elistX0;
|
||||
for (elistX0 = listX0; elistX0; elistX0 = elistX0->next) {
|
||||
@@ -2208,7 +2237,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
flattenInstancesOf(name1, file1, ecompX0->cell1->name);
|
||||
ecompX0->num1 = 0;
|
||||
ecomp0X->num1 += ecompX0->num1;
|
||||
modified++;
|
||||
modified1++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2234,7 +2263,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
flattenInstancesOf(name2, file2, ecomp0X->cell2->name);
|
||||
ecomp0X->num2 = 0;
|
||||
ecompX0->num2 += ecomp0X->num2;
|
||||
modified++;
|
||||
modified2++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2265,5 +2294,17 @@ done:
|
||||
FREE(list0X);
|
||||
list0X = nextptr;
|
||||
}
|
||||
return modified;
|
||||
|
||||
// If either netlist was modified, rebuild its node cache
|
||||
|
||||
if (modified1 > 0) {
|
||||
FreeNodeNames(tc1);
|
||||
CacheNodeNames(tc1);
|
||||
}
|
||||
if (modified2 > 0) {
|
||||
FreeNodeNames(tc2);
|
||||
CacheNodeNames(tc2);
|
||||
}
|
||||
|
||||
return modified1 + modified2;
|
||||
}
|
||||
|
||||
+403
-253
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -62,7 +62,7 @@ extern void RegroupDataStructures();
|
||||
extern void FormatIllegalElementClasses();
|
||||
extern void FormatIllegalNodeClasses();
|
||||
extern int ResolveAutomorphsByProperty();
|
||||
extern int ResolveAutomorphsByPin();
|
||||
extern int ResolveAutomorphsByPin(int match_nets);
|
||||
extern void SummarizeElementClasses(struct ElementClass *EC);
|
||||
extern int remove_group_tags(struct objlist *ob);
|
||||
|
||||
|
||||
+26
-11
@@ -587,6 +587,12 @@ void SkipTokNoNewline(char *delimiter)
|
||||
/* */
|
||||
/* Modified 3/30/2015 to include the condition where a comment line is */
|
||||
/* in the middle of a series of continuation lines. */
|
||||
/* */
|
||||
/* Modified 1/3/2024 to avoid skipping two lines if a line has only the */
|
||||
/* comment character '*' followed by a newline. It seems that '\n' is */
|
||||
/* being ignored in WHITESPACE_DELIMITER, but it's easier to write the */
|
||||
/* code to find the exception rather than track down the problem in */
|
||||
/* GetNextLine(). */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void SpiceTokNoNewline(void)
|
||||
@@ -598,8 +604,14 @@ void SpiceTokNoNewline(void)
|
||||
while (nexttok == NULL) {
|
||||
contline = getc(infile);
|
||||
if (contline == '*') {
|
||||
GetNextLine(WHITESPACE_DELIMITER);
|
||||
SkipNewLine(NULL);
|
||||
char testline = ' ';
|
||||
while ((testline == ' ') || (testline == '\t'))
|
||||
testline = getc(infile);
|
||||
if (testline != '\n') {
|
||||
ungetc(testline, infile);
|
||||
GetNextLine(WHITESPACE_DELIMITER);
|
||||
SkipNewLine(NULL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (contline != '+') {
|
||||
@@ -886,7 +898,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 +906,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 +924,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},
|
||||
|
||||
@@ -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"
|
||||
|
||||
+8
-4
@@ -300,7 +300,9 @@ int ReduceOneExpression(struct valuelist *kv, struct objlist *parprops,
|
||||
tstr = sstr - 1;
|
||||
numlast = 1;
|
||||
}
|
||||
break;
|
||||
/* But might not be. . . */
|
||||
if ((dval != 0) || (sstr > estr))
|
||||
break;
|
||||
}
|
||||
/* Not a number, so must be arithmetic */
|
||||
*tstr = '\0';
|
||||
@@ -320,7 +322,9 @@ int ReduceOneExpression(struct valuelist *kv, struct objlist *parprops,
|
||||
tstr = sstr - 1;
|
||||
numlast = 1;
|
||||
}
|
||||
break;
|
||||
/* But might not be. . . */
|
||||
if ((dval != 0) || (sstr > estr))
|
||||
break;
|
||||
}
|
||||
/* Not a number, so must be arithmetic */
|
||||
*tstr = '\0';
|
||||
@@ -3090,7 +3094,7 @@ void add_balancing_close(struct objlist *ob1, struct objlist *ob2)
|
||||
for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
|
||||
if (nob->type == PROPERTY)
|
||||
break;
|
||||
if (nob->type != PROPERTY) return; // shouldn't happen
|
||||
if (nob == NULL || nob->type != PROPERTY) return; // shouldn't happen
|
||||
|
||||
opentags = 0;
|
||||
for (; nob->next && nob->next->type == PROPERTY; nob = nob->next) {
|
||||
@@ -3640,7 +3644,7 @@ int CombineSeries(char *model, int file)
|
||||
nob->type = PROPERTY;
|
||||
nob->name = strsave("properties");
|
||||
nob->node = -2; /* Don't report as disconnected node */
|
||||
nob->model.class = (obp->model.class == NULL) ? NULL :
|
||||
nob->model.class = (obp == NULL || obp->model.class == NULL) ? NULL :
|
||||
strsave(obp->model.class);
|
||||
nob->instance.props = NewPropValue(2);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+91
-68
@@ -223,12 +223,35 @@ int matchnocase(char *st1, char *st2)
|
||||
{
|
||||
char *sp1 = st1;
|
||||
char *sp2 = st2;
|
||||
char v1 = FALSE, v2 = FALSE;
|
||||
|
||||
/* In case of a property that does not exist in one netlist, matchnocase()
|
||||
* may be passed a null value, so return 0 to indicate a non-match.
|
||||
* *Both* values null will also be treated as a mismatch (debatable
|
||||
* behavior).
|
||||
*/
|
||||
if (!sp1 || !sp2) return 0;
|
||||
|
||||
/* Verilog back-slash escaped names should match an equivalent non-
|
||||
* back-slashed name. (NOTE: This behavior needs to be added to match().)
|
||||
*/
|
||||
if ((*sp1 == '\\') && (*sp2 != '\\')) {
|
||||
v1 = TRUE;
|
||||
sp1++;
|
||||
}
|
||||
if ((*sp2 == '\\') && (*sp1 != '\\')) {
|
||||
v2 = TRUE;
|
||||
sp2++;
|
||||
}
|
||||
|
||||
while (*sp1 != '\0' && *sp2 != '\0') {
|
||||
if (to_lower[*sp1] != to_lower[*sp2]) break;
|
||||
sp1++;
|
||||
sp2++;
|
||||
}
|
||||
if (v1 && (*sp1 == ' ')) sp1++;
|
||||
if (v2 && (*sp2 == ' ')) sp2++;
|
||||
|
||||
if ((*sp1 != '\0') || (*sp2 != '\0')) return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -596,65 +619,65 @@ int freeprop(struct hashlist *p)
|
||||
|
||||
void CellDelete(char *name, int fnum)
|
||||
{
|
||||
/* delete all the contents of cell 'name', and remove 'name' from
|
||||
the cell hash table. NOTE: this procedure does not care or check
|
||||
if 'name' has been instanced anywhere. It is assumed that if this
|
||||
is the case, the user will (quickly) define a new cell of that name.
|
||||
*/
|
||||
struct objlist *ob, *obnext;
|
||||
struct nlist *tp;
|
||||
/* delete all the contents of cell 'name', and remove 'name' from
|
||||
the cell hash table. NOTE: this procedure does not care or check
|
||||
if 'name' has been instanced anywhere. It is assumed that if this
|
||||
is the case, the user will (quickly) define a new cell of that name.
|
||||
*/
|
||||
struct objlist *ob, *obnext;
|
||||
struct nlist *tp;
|
||||
|
||||
tp = LookupCellFile(name, fnum);
|
||||
if (tp == NULL) {
|
||||
Printf ("No cell '%s' found.\n", name);
|
||||
return;
|
||||
}
|
||||
tp = LookupCellFile(name, fnum);
|
||||
if (tp == NULL) {
|
||||
Printf ("No cell '%s' found.\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
HashIntDelete(name, fnum, &cell_dict);
|
||||
/* now make sure that we free all the fields of the nlist struct */
|
||||
if (tp->name != NULL) FREE(tp->name);
|
||||
HashKill(&(tp->objdict));
|
||||
HashKill(&(tp->instdict));
|
||||
RecurseHashTable(&(tp->propdict), freeprop);
|
||||
HashKill(&(tp->propdict));
|
||||
FreeNodeNames(tp);
|
||||
ob = tp->cell;
|
||||
while (ob != NULL) {
|
||||
obnext = ob->next;
|
||||
FreeObject (ob);
|
||||
ob = obnext;
|
||||
}
|
||||
HashIntDelete(name, fnum, &cell_dict);
|
||||
/* now make sure that we free all the fields of the nlist struct */
|
||||
if (tp->name != NULL) FREE(tp->name);
|
||||
HashKill(&(tp->objdict));
|
||||
HashKill(&(tp->instdict));
|
||||
RecurseHashTable(&(tp->propdict), freeprop);
|
||||
HashKill(&(tp->propdict));
|
||||
FreeNodeNames(tp);
|
||||
ob = tp->cell;
|
||||
while (ob != NULL) {
|
||||
obnext = ob->next;
|
||||
FreeObject (ob);
|
||||
ob = obnext;
|
||||
}
|
||||
}
|
||||
|
||||
static int PrintCellHashTableElement(struct hashlist *p)
|
||||
{
|
||||
struct nlist *ptr;
|
||||
struct nlist *ptr;
|
||||
|
||||
ptr = (struct nlist *)(p->ptr);
|
||||
if ((TopFile >= 0) && (ptr->file != TopFile)) return 1;
|
||||
ptr = (struct nlist *)(p->ptr);
|
||||
if ((TopFile >= 0) && (ptr->file != TopFile)) return 1;
|
||||
|
||||
if (ptr->class != CLASS_SUBCKT) {
|
||||
/* only print primitive cells if Debug is enabled */
|
||||
if (Debug == 1) Printf("Cell: %s (instanced %d times); Primitive\n",
|
||||
if ((ptr->class != CLASS_SUBCKT) && (ptr->class != CLASS_MODULE)) {
|
||||
/* only print primitive cells if Debug is enabled */
|
||||
if (Debug == 1) Printf("Cell: %s (instanced %d times); Primitive\n",
|
||||
ptr->name, ptr->number);
|
||||
else if (Debug == 3) { /* list */
|
||||
else if (Debug == 3) { /* list */
|
||||
#ifdef TCL_NETGEN
|
||||
Tcl_AppendElement(netgeninterp, ptr->name);
|
||||
Tcl_AppendElement(netgeninterp, ptr->name);
|
||||
#else
|
||||
Printf("%s ", ptr->name);
|
||||
Printf("%s ", ptr->name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if ((Debug == 2) || (Debug == 3)) { /* list only */
|
||||
}
|
||||
}
|
||||
else if ((Debug == 2) || (Debug == 3)) { /* list only */
|
||||
#ifdef TCL_NETGEN
|
||||
Tcl_AppendElement(netgeninterp, ptr->name);
|
||||
Tcl_AppendElement(netgeninterp, ptr->name);
|
||||
#else
|
||||
Printf("%s ", ptr->name);
|
||||
Printf("%s ", ptr->name);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
Printf("Cell: %s (instanced %d times)\n",ptr->name,ptr->number);
|
||||
return(1);
|
||||
}
|
||||
else
|
||||
Printf("Cell: %s (instanced %d times)\n", ptr->name, ptr->number);
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* Print the contents of the cell hash table. */
|
||||
@@ -663,65 +686,65 @@ static int PrintCellHashTableElement(struct hashlist *p)
|
||||
|
||||
void PrintCellHashTable(int full, int filenum)
|
||||
{
|
||||
int total, bins;
|
||||
int OldDebug;
|
||||
int total, bins;
|
||||
int OldDebug;
|
||||
|
||||
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
||||
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
||||
PrintCellHashTable(full, Circuit1->file);
|
||||
PrintCellHashTable(full, Circuit2->file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TopFile = filenum;
|
||||
TopFile = filenum;
|
||||
|
||||
bins = RecurseHashTable(&cell_dict, CountHashTableBinsUsed);
|
||||
total = RecurseHashTable(&cell_dict, CountHashTableEntries);
|
||||
if (full < 2)
|
||||
Printf("Hash table: %d of %d bins used; %d cells total (%.2f per bin)\n",
|
||||
bins = RecurseHashTable(&cell_dict, CountHashTableBinsUsed);
|
||||
total = RecurseHashTable(&cell_dict, CountHashTableEntries);
|
||||
if (full < 2)
|
||||
Printf("Hash table: %d of %d bins used; %d cells total (%.2f per bin)\n",
|
||||
bins, CELLHASHSIZE, total, (bins == 0) ? 0 :
|
||||
(float)((float)total / (float)bins));
|
||||
|
||||
OldDebug = Debug;
|
||||
Debug = full;
|
||||
RecurseHashTable(&cell_dict, PrintCellHashTableElement);
|
||||
Debug = OldDebug;
|
||||
OldDebug = Debug;
|
||||
Debug = full;
|
||||
RecurseHashTable(&cell_dict, PrintCellHashTableElement);
|
||||
Debug = OldDebug;
|
||||
#ifndef TCL_NETGEN
|
||||
if (full >= 2) Printf("\n");
|
||||
if (full >= 2) Printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
struct nlist *FirstCell(void)
|
||||
{
|
||||
return((struct nlist *)HashFirst(&cell_dict));
|
||||
return((struct nlist *)HashFirst(&cell_dict));
|
||||
}
|
||||
|
||||
struct nlist *NextCell(void)
|
||||
{
|
||||
return((struct nlist *)HashNext(&cell_dict));
|
||||
return((struct nlist *)HashNext(&cell_dict));
|
||||
}
|
||||
|
||||
static int ClearDumpedElement(struct hashlist *np)
|
||||
{
|
||||
struct nlist *p;
|
||||
struct nlist *p;
|
||||
|
||||
p = (struct nlist *)(np->ptr);
|
||||
p->dumped = 0;
|
||||
return(1);
|
||||
p = (struct nlist *)(np->ptr);
|
||||
p->dumped = 0;
|
||||
return(1);
|
||||
}
|
||||
|
||||
void ClearDumpedList(void)
|
||||
{
|
||||
RecurseHashTable(&cell_dict, ClearDumpedElement);
|
||||
RecurseHashTable(&cell_dict, ClearDumpedElement);
|
||||
}
|
||||
|
||||
int RecurseCellHashTable(int (*foo)(struct hashlist *np))
|
||||
{
|
||||
return RecurseHashTable(&cell_dict, foo);
|
||||
return RecurseHashTable(&cell_dict, foo);
|
||||
}
|
||||
|
||||
int RecurseCellFileHashTable(int (*foo)(struct hashlist *, int), int value)
|
||||
{
|
||||
return RecurseHashTableValue(&cell_dict, foo, value);
|
||||
return RecurseHashTableValue(&cell_dict, foo, value);
|
||||
}
|
||||
|
||||
/* Yet another version, passing one parameter that is a pointer */
|
||||
@@ -729,7 +752,7 @@ int RecurseCellFileHashTable(int (*foo)(struct hashlist *, int), int value)
|
||||
struct nlist *RecurseCellHashTable2(struct nlist *(*foo)(struct hashlist *,
|
||||
void *), void *pointer)
|
||||
{
|
||||
return RecurseHashTablePointer(&cell_dict, foo, pointer);
|
||||
return RecurseHashTablePointer(&cell_dict, foo, pointer);
|
||||
}
|
||||
|
||||
/************************** WILD-CARD STUFF *******************************/
|
||||
|
||||
+75
-6
@@ -43,6 +43,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
#include "print.h"
|
||||
#include "query.h"
|
||||
#include "objlist.h"
|
||||
#include "netcmp.h"
|
||||
|
||||
// Global storage for parameters from .PARAM
|
||||
struct hashdict spiceparams;
|
||||
@@ -519,7 +520,7 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
char *eqptr, devtype, in_subckt;
|
||||
struct keyvalue *kvlist = NULL;
|
||||
char inst[MAX_STR_LEN], model[MAX_STR_LEN], instname[MAX_STR_LEN];
|
||||
struct nlist *tp;
|
||||
struct nlist *tp, *tpsave;
|
||||
struct objlist *parent, *sobj, *nobj, *lobj, *pobj;
|
||||
|
||||
inst[MAX_STR_LEN-1] = '\0';
|
||||
@@ -557,6 +558,7 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
|
||||
snprintf(model, MAX_STR_LEN-1, "%s", nexttok);
|
||||
tp = LookupCellFile(nexttok, filenum);
|
||||
tpsave = NULL;
|
||||
|
||||
/* Check for name conflict with duplicate cell names */
|
||||
/* This may mean that the cell was used before it was */
|
||||
@@ -595,11 +597,46 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
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 */
|
||||
CellDef(model, filenum);
|
||||
tp = LookupCellFile(model, filenum);
|
||||
update = 1; /* Will need to update existing instances */
|
||||
/* Handle issue with SPICE read after verilog, where a placeholder
|
||||
* was created from the verilog. (1) If the pin names are "1", "2",
|
||||
* "3", then this is a SPICE placeholder, and just remove the CellDef
|
||||
* and re-create it. Otherwise, create new cell "_PLACEHOLDER_".
|
||||
* (2) After encountering .ends, run MatchPins between the two cells.
|
||||
* (3) delete the original cell and rename the new cell.
|
||||
*/
|
||||
int i = 1;
|
||||
char pname[10];
|
||||
for (pobj = tp->cell; pobj && pobj->type == PORT; pobj = pobj->next) {
|
||||
sprintf(pname, "%d", i);
|
||||
if (!matchnocase(pobj->name, pname)) break;
|
||||
i++;
|
||||
}
|
||||
if ((pobj == NULL) || (pobj->type != PORT)) {
|
||||
/* This is a SPICE placeholder created because the cell was instanced
|
||||
* before it was defined. However, the pins can be assumed to be in
|
||||
* the correct order, and pin reordering does not need to be done.
|
||||
*/
|
||||
FreePorts(nexttok);
|
||||
CellDelete(nexttok, filenum); /* This removes any PLACEHOLDER flag */
|
||||
CellDef(model, filenum);
|
||||
tp = LookupCellFile(model, filenum);
|
||||
update = 1; /* Will need to update existing instances */
|
||||
}
|
||||
else {
|
||||
/* This is (probably) a verilog placeholder created because the
|
||||
* verilog was read before the (SPICE) definitions. The verilog
|
||||
* netlist should have named the pins of the parent cell. However,
|
||||
* there is no guarantee the order of pins is correct. The MatchPins()
|
||||
* routine from netcmp.c can be used here to match the cell against
|
||||
* the placeholder, and reorder the pins in all instances to match.
|
||||
* Note that we cannot just reorder the SPICE pins to match the
|
||||
* verilog order, because there may be other SPICE netlists which
|
||||
* instance the cell with the correct SPICE port order.
|
||||
*/
|
||||
tpsave = tp;
|
||||
CellDef("_PLACEHOLDER_", filenum);
|
||||
tp = LookupCellFile("_PLACEHOLDER_", filenum);
|
||||
}
|
||||
}
|
||||
else if (tp == NULL) { /* Completely new cell, no name conflict */
|
||||
CellDef(model, filenum);
|
||||
@@ -691,6 +728,38 @@ skip_ends:
|
||||
if (*CellStackPtr) PopStack(CellStackPtr);
|
||||
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
||||
SkipNewLine(NULL);
|
||||
|
||||
if (tpsave != NULL) {
|
||||
struct nlist *tpplace;
|
||||
char *savename;
|
||||
|
||||
/* Handle a placeholder from a verilog file that has been replaced
|
||||
* by a netlist with pins in a different order. The pins need to
|
||||
* be matched, corrected in the original cell and all instances,
|
||||
* and the new cell deleted.
|
||||
*/
|
||||
|
||||
Printf("Verilog placeholder %s replaced by SPICE definition\n",
|
||||
tpsave->name);
|
||||
tpplace = LookupCellFile("_PLACEHOLDER_", filenum);
|
||||
/* MatchPins is part of netcmp and normally Circuit2 is the
|
||||
* circuit being matched, so set Circuit2 to the original
|
||||
* verilog black-box cell, and MatchPins() will force its
|
||||
* pins to be rearranged to match the SPICE definition just
|
||||
* read.
|
||||
*/
|
||||
Circuit2 = tpsave;
|
||||
MatchPins(tpplace, tpsave, 0);
|
||||
savename = strsave(tpsave->name);
|
||||
/* Now the original verilog black-box cell can be removed */
|
||||
FreePorts(savename);
|
||||
CellDelete(savename, filenum);
|
||||
/* And _PLACEHOLDER_ is renamed to the original name of the cell. */
|
||||
CellRehash("_PLACEHOLDER_", savename, filenum);
|
||||
tpsave = NULL;
|
||||
Circuit2 = NULL;
|
||||
FREE(savename);
|
||||
}
|
||||
}
|
||||
else if (matchnocase(nexttok, ".MODEL")) {
|
||||
unsigned char class = CLASS_SUBCKT;
|
||||
|
||||
+110
-34
@@ -57,6 +57,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
#include "netfile.h"
|
||||
#include "print.h"
|
||||
#include "hash.h"
|
||||
#include "netcmp.h"
|
||||
|
||||
// See netfile.c for explanation of delimiters. 'X'
|
||||
// separates single-character delimiters from two-character delimiters.
|
||||
@@ -716,10 +717,12 @@ int GetBus(char *astr, struct bus *wb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Output a Verilog Module. Note that since Verilog does not describe
|
||||
// low-level devices like transistors, capacitors, etc., then this
|
||||
// format is limited to black-box subcircuits. Cells containing any
|
||||
// such low-level devices are ignored.
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
void VerilogModule(struct nlist *tp)
|
||||
{
|
||||
@@ -1018,7 +1021,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
char *eqptr, *matchptr;
|
||||
struct keyvalue *kvlist = NULL;
|
||||
char inst[MAX_STR_LEN], model[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN];
|
||||
struct nlist *tp;
|
||||
struct nlist *tp, *tpsave;
|
||||
struct objlist *parent, *sobj, *nobj, *lobj, *pobj, *cref;
|
||||
|
||||
inst[MAX_STR_LEN-1] = '\0';
|
||||
@@ -1152,6 +1155,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
|
||||
snprintf(model, MAX_STR_LEN-1, "%s", nexttok);
|
||||
tp = LookupCellFile(nexttok, filenum);
|
||||
tpsave = NULL;
|
||||
hasports = (char)0;
|
||||
|
||||
/* Check for name conflict with duplicate cell names */
|
||||
@@ -1191,35 +1195,9 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
tp = LookupCellFile(nexttok, filenum);
|
||||
}
|
||||
else if (tp != NULL) { /* Cell exists, but as a placeholder */
|
||||
struct nlist *tptmp = NULL;
|
||||
char ctemp[8];
|
||||
int n = 0;
|
||||
|
||||
/* This redefines a placeholder module to an unused temporary cell name */
|
||||
while (1) {
|
||||
sprintf(ctemp, "%d", n);
|
||||
tptmp = LookupCellFile(ctemp, filenum);
|
||||
if (tptmp == NULL) break;
|
||||
n++;
|
||||
}
|
||||
CellRehash(nexttok, ctemp, filenum);
|
||||
tptmp = LookupCellFile(ctemp, filenum);
|
||||
|
||||
/* Create a new module definition */
|
||||
CellDef(model, filenum);
|
||||
tp = LookupCellFile(model, filenum);
|
||||
|
||||
/* Find an instance of this module in the netlist */
|
||||
cref = FindInstanceOf(tp);
|
||||
if ((cref != NULL) && (cref->name != NULL)) {
|
||||
hasports = (char)1;
|
||||
/* Copy ports from the original parent cell to the new parent cell */
|
||||
for (pobj = tptmp->cell; pobj && (pobj->type == PORT); pobj = pobj->next)
|
||||
Port(pobj->name);
|
||||
}
|
||||
/* Remove the original cell definition */
|
||||
FreePorts(ctemp);
|
||||
CellDelete(ctemp, filenum); /* This removes any PLACEHOLDER flag */
|
||||
tpsave = tp;
|
||||
CellDef("_PLACEHOLDER_", filenum);
|
||||
tp = LookupCellFile("_PLACEHOLDER_", filenum);
|
||||
}
|
||||
else if (tp == NULL) { /* Completely new cell, no name conflict */
|
||||
CellDef(model, filenum);
|
||||
@@ -1428,6 +1406,93 @@ skip_endmodule:
|
||||
if (*CellStackPtr) PopStack(CellStackPtr);
|
||||
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
||||
SkipNewLine(VLOG_DELIMITERS);
|
||||
|
||||
if (tpsave != NULL) {
|
||||
struct nlist *tpplace;
|
||||
char *savename;
|
||||
int lnum, pnum, ltest;
|
||||
unsigned char valid;
|
||||
struct objlist *lobj, *pobj;
|
||||
|
||||
/* Handle a placeholder from a verilog file that has been replaced
|
||||
* by a netlist with pins in a different order. The pins need to
|
||||
* be matched, corrected in the original cell and all instances,
|
||||
* and the new cell deleted.
|
||||
*/
|
||||
|
||||
Printf("Verilog placeholder module %s replaced by module definition\n",
|
||||
tpsave->name);
|
||||
tpplace = LookupCellFile("_PLACEHOLDER_", filenum);
|
||||
|
||||
/* If tpsave was generated from an instance in a SPICE netlist that
|
||||
* did not have a black-box subcircuit definition, then the pins
|
||||
* will all be labeled 1, 2, 3, etc. If so, then assume that the
|
||||
* verilog pins are in order, and rename the placeholder pins.
|
||||
* If the number of pins does not match, or if the pins are not
|
||||
* labeled as ascending integers, then leave the cell alone.
|
||||
* In either case, output a warning message.
|
||||
*/
|
||||
|
||||
/* Get the number of ports in the placeholder */
|
||||
pnum = 0;
|
||||
for (pobj = tpplace->cell; pobj; pobj = pobj->next) {
|
||||
if (pobj->type != PORT) break;
|
||||
pnum++;
|
||||
}
|
||||
|
||||
/* Get the number of ports in the saved cell and make */
|
||||
/* sure that it equals the number of ports in the */
|
||||
/* placeholder, and that all of the ports in the saved */
|
||||
/* cell are integers in ascending order. */
|
||||
|
||||
valid = TRUE;
|
||||
lnum = 0;
|
||||
for (lobj = tpsave->cell; lobj; lobj = lobj->next) {
|
||||
if (lobj->type != PORT) break;
|
||||
lnum++;
|
||||
if (sscanf(lobj->name, "%d", <est) != 1) break;
|
||||
if (ltest != lnum) break;
|
||||
}
|
||||
if ((lobj != NULL) && (lobj->type == PORT))
|
||||
valid = FALSE; /* Pins are not integers in ascending order */
|
||||
if (pnum != lnum) valid = FALSE; /* Different number of pins */
|
||||
|
||||
if (valid == TRUE) {
|
||||
Printf("Replacing pins of placeholder cell %s from cell definition.\n",
|
||||
tpsave->name);
|
||||
pobj = tpplace->cell;
|
||||
for (lobj = tpsave->cell; lobj; lobj = lobj->next) {
|
||||
if (lobj->type != PORT) break;
|
||||
if (pobj == NULL) break; /* should not happen */
|
||||
FREE(lobj->name);
|
||||
lobj->name = (char *)MALLOC(strlen(pobj->name) + 1);
|
||||
strcpy(lobj->name, pobj->name);
|
||||
pobj = pobj->next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Printf("Placeholder pins of cell %s are not compatible and"
|
||||
" will be left unchanged\n", tpsave->name);
|
||||
}
|
||||
|
||||
/* MatchPins is part of netcmp and normally Circuit2 is the
|
||||
* circuit being matched, so set Circuit2 to the original
|
||||
* verilog black-box cell, and MatchPins() will force its
|
||||
* pins to be rearranged to match the module definition just
|
||||
* read.
|
||||
*/
|
||||
Circuit2 = tpsave;
|
||||
MatchPins(tpplace, tpsave, 0);
|
||||
savename = strsave(tpsave->name);
|
||||
/* Now the original verilog black-box cell can be removed */
|
||||
FreePorts(savename);
|
||||
CellDelete(savename, filenum);
|
||||
/* And _PLACEHOLDER_ is renamed to the original name of the cell. */
|
||||
CellRehash("_PLACEHOLDER_", savename, filenum);
|
||||
tpsave = NULL;
|
||||
Circuit2 = NULL;
|
||||
FREE(savename);
|
||||
}
|
||||
}
|
||||
|
||||
else if (match(nexttok, "`include")) {
|
||||
@@ -1770,8 +1835,9 @@ skip_endmodule:
|
||||
}
|
||||
else { /* "assign" */
|
||||
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
||||
if (GetBusTok(&wb) == 0) {
|
||||
char *aptr = strvchr(nexttok, '[');
|
||||
char *aptr = strvchr(nexttok, '[');
|
||||
if (((aptr == NULL) && (GetBusTok(&wb) == 0)) ||
|
||||
((aptr != NULL) && (GetBus(aptr, &wb) == 0))) {
|
||||
if (aptr != NULL) {
|
||||
*aptr = '\0';
|
||||
/* Find object of first net in bus */
|
||||
@@ -1789,6 +1855,15 @@ skip_endmodule:
|
||||
}
|
||||
else {
|
||||
lhs = LookupObject(nexttok, CurrentCell);
|
||||
/* Handle the case in which an assignment is made
|
||||
* without first declaring a wire for the signal,
|
||||
* which is considered valid syntax (patch by
|
||||
* Sylvain Munaut).
|
||||
*/
|
||||
if (lhs == NULL) {
|
||||
Node(nexttok);
|
||||
lhs = LookupObject(nexttok, CurrentCell);
|
||||
}
|
||||
strcpy(noderoot, nexttok);
|
||||
}
|
||||
SkipTokComments(VLOG_DELIMITERS);
|
||||
@@ -1837,8 +1912,9 @@ skip_endmodule:
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (GetBusTok(&wb2) == 0) {
|
||||
char *aptr = strvchr(nexttok, '[');
|
||||
char *aptr = strvchr(nexttok, '[');
|
||||
if (((aptr == NULL) && (GetBusTok(&wb2) == 0)) ||
|
||||
((aptr != NULL) && (GetBus(aptr, &wb2) == 0))) {
|
||||
j = wb2.start;
|
||||
if (aptr != NULL) {
|
||||
*aptr = '\0';
|
||||
|
||||
Vendored
+1
-52
@@ -668,7 +668,6 @@ XMKMF
|
||||
HAVE_PYTHON3
|
||||
EGREP
|
||||
GREP
|
||||
M4
|
||||
RANLIB
|
||||
INSTALL_DATA
|
||||
INSTALL_SCRIPT
|
||||
@@ -3702,56 +3701,6 @@ else
|
||||
fi
|
||||
|
||||
|
||||
for ac_prog in gm4 gnum4 m4
|
||||
do
|
||||
# Extract the first word of "$ac_prog", so it can be a program name with args.
|
||||
set dummy $ac_prog; ac_word=$2
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
|
||||
$as_echo_n "checking for $ac_word... " >&6; }
|
||||
if ${ac_cv_path_M4+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
case $M4 in
|
||||
[\\/]* | ?:[\\/]*)
|
||||
ac_cv_path_M4="$M4" # Let the user override the test with a path.
|
||||
;;
|
||||
*)
|
||||
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
|
||||
for as_dir in $PATH
|
||||
do
|
||||
IFS=$as_save_IFS
|
||||
test -z "$as_dir" && as_dir=.
|
||||
for ac_exec_ext in '' $ac_executable_extensions; do
|
||||
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
|
||||
ac_cv_path_M4="$as_dir/$ac_word$ac_exec_ext"
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
IFS=$as_save_IFS
|
||||
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
M4=$ac_cv_path_M4
|
||||
if test -n "$M4"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $M4" >&5
|
||||
$as_echo "$M4" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
fi
|
||||
|
||||
|
||||
test -n "$M4" && break
|
||||
done
|
||||
test -n "$M4" || M4="no"
|
||||
|
||||
if test x$M4 = xno; then
|
||||
as_fn_error $? "M4 is required" "$LINENO" 5
|
||||
fi
|
||||
|
||||
|
||||
if test "$CPP" = "$CC -E" ; then
|
||||
CPP="$CPP -x c"
|
||||
@@ -6302,7 +6251,7 @@ fi
|
||||
|
||||
*darwin*)
|
||||
SHDLIB_EXT=".dylib"
|
||||
LDDL_FLAGS="-dynamiclib -flat_namespace -undefined suppress -noprebind"
|
||||
LDDL_FLAGS="-dynamiclib -undefined dynamic_lookup"
|
||||
LDFLAGS="${LDFLAGS} ${LIB_SPECS}"
|
||||
CFLAGS="${CFLAGS} ${X_CFLAGS} ${INC_SPECS} -I/sw/include -fno-common"
|
||||
;;
|
||||
|
||||
@@ -26,12 +26,6 @@ AC_ISC_POSIX
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_RANLIB
|
||||
|
||||
dnl GNU M4 is preferred due to some of the option switches.
|
||||
AC_PATH_PROGS([M4], [gm4 gnum4 m4], [no])
|
||||
if test x$M4 = xno; then
|
||||
AC_MSG_ERROR([M4 is required])
|
||||
fi
|
||||
|
||||
dnl check size of pointer for correct behavior on 64-bit systems
|
||||
dnl If the C preprocessor is GCC, we need to force the flag to
|
||||
dnl assert that input files are of type C, or else the preprocessing
|
||||
@@ -1033,7 +1027,7 @@ fi
|
||||
|
||||
*darwin*)
|
||||
SHDLIB_EXT=".dylib"
|
||||
LDDL_FLAGS="-dynamiclib -flat_namespace -undefined suppress -noprebind"
|
||||
LDDL_FLAGS="-dynamiclib -undefined dynamic_lookup"
|
||||
LDFLAGS="${LDFLAGS} ${LIB_SPECS}"
|
||||
CFLAGS="${CFLAGS} ${X_CFLAGS} ${INC_SPECS} -I/sw/include -fno-common"
|
||||
;;
|
||||
|
||||
@@ -741,6 +741,19 @@ set auto_noexec 1 ;# don't EVER call UNIX commands w/o "shell" in front
|
||||
# Cross-Application section
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# For use with open_pdks, set PDK_ROOT from the environment. If no
|
||||
# such environment variable exists, check some common locations.
|
||||
|
||||
if {[catch {set PDK_ROOT $::env(PDK_ROOT)}]} {
|
||||
if {[file isdir /usr/local/share/pdk] == 1} {
|
||||
set PDK_ROOT /usr/local/share/pdk
|
||||
} elseif {[file isdir /usr/share/pdk] == 1} {
|
||||
set PDK_ROOT /usr/share/pdk
|
||||
} elseif {[file isdir /foss/pdk] == 1} {
|
||||
set PDK_ROOT /foss/pdk
|
||||
}
|
||||
}
|
||||
|
||||
# Setup IRSIM assuming that the Tcl version is installed.
|
||||
# We do not need to rename procedure irsim to NULL because it is
|
||||
# redefined in a script, which simply overwrites the original.
|
||||
|
||||
+26
-8
@@ -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;
|
||||
@@ -1592,8 +1595,7 @@ _netgen_model(ClientData clientData,
|
||||
return result;
|
||||
|
||||
if (objc == 3) {
|
||||
model = Tcl_GetString(objv[2]);
|
||||
nports = NumberOfPorts(model, fnum);
|
||||
nports = NumberOfPorts(tp->name, fnum);
|
||||
|
||||
if (Tcl_GetIndexFromObj(interp, objv[2], (CONST84 char **)modelclasses,
|
||||
"class", 0, &index) != TCL_OK) {
|
||||
@@ -2510,7 +2512,13 @@ _netcmp_run(ClientData clientData,
|
||||
if (automorphisms > 0) {
|
||||
// Next, attempt to resolve automorphisms uniquely by
|
||||
// using the pin names
|
||||
automorphisms = ResolveAutomorphsByPin();
|
||||
automorphisms = ResolveAutomorphsByPin(FALSE);
|
||||
}
|
||||
if (automorphisms > 0) {
|
||||
// Next, attempt to resolve automorphisms uniquely by
|
||||
// using the net names (should only be done after
|
||||
// resolving by pin).
|
||||
automorphisms = ResolveAutomorphsByPin(TRUE);
|
||||
}
|
||||
if (automorphisms > 0) {
|
||||
// Anything left is truly indistinguishable
|
||||
@@ -2520,8 +2528,16 @@ _netcmp_run(ClientData clientData,
|
||||
|
||||
if (automorphisms == -1)
|
||||
Fprintf(stdout, "Netlists do not match.\n");
|
||||
else if (automorphisms == -2)
|
||||
Fprintf(stdout, "Netlists match uniquely with port errors.\n");
|
||||
else if (automorphisms == -2) {
|
||||
Fprintf(stdout, "Netlists match uniquely with port");
|
||||
if (PropertyErrorDetected) {
|
||||
Fprintf(stdout, " and property errors.\n");
|
||||
PrintPropertyResults(dolist);
|
||||
}
|
||||
else {
|
||||
Fprintf(stdout, " errors.\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (automorphisms == 0)
|
||||
Fprintf(stdout, "Netlists match uniquely");
|
||||
@@ -2649,8 +2665,10 @@ _netcmp_verify(ClientData clientData,
|
||||
disable_interrupt();
|
||||
if (index == EQUIV_IDX || index == UNIQUE_IDX)
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
|
||||
else
|
||||
else {
|
||||
Fprintf(stdout, "Netlists do not match.\n");
|
||||
Fprintf(stdout, "Port matching may fail to disambiguate symmetries.\n");
|
||||
}
|
||||
}
|
||||
else if (automorphisms == -2) {
|
||||
if (index == EQUIV_IDX)
|
||||
|
||||
Reference in New Issue
Block a user