mirror of
https://github.com/RTimothyEdwards/netgen.git
synced 2026-08-22 14:07:07 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cbf15aab1 | ||
|
|
b59196fa81 | ||
|
|
1de6f88f1e | ||
|
|
6e6e9fb73f | ||
|
|
33fed391fd | ||
|
|
e84700a607 | ||
|
|
5f5248b3d0 | ||
|
|
0bee21ccc8 | ||
|
|
80f9263004 | ||
|
|
c269f1de89 | ||
|
|
edb50746cb | ||
|
|
4443826f9e | ||
|
|
f2368ca223 | ||
|
|
a60dac6124 | ||
|
|
ee93d52a26 | ||
|
|
bbe645f0ab | ||
|
|
ba7004fd5b | ||
|
|
4f315d33d6 | ||
|
|
704bfbc871 | ||
|
|
4457248ecd | ||
|
|
7bee1851fa | ||
|
|
021dfa6e8a | ||
|
|
e4a4621b96 | ||
|
|
1d286f9973 | ||
|
|
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 | ||
|
|
9b0afa893d | ||
|
|
f59c9ebcb7 | ||
|
|
c0f7ebd625 | ||
|
|
ce097d5d76 | ||
|
|
619409556c | ||
|
|
b1374e2bc8 | ||
|
|
9e9288f746 | ||
|
|
cff954f36a | ||
|
|
c27d933adc | ||
|
|
bcbc736b51 | ||
|
|
615c55cbe2 | ||
|
|
f5f1240073 | ||
|
|
87d8759a69 | ||
|
|
02d7a1bd01 | ||
|
|
1efa054ac1 | ||
|
|
7878b3cfbc | ||
|
|
609d1de250 | ||
|
|
7f727e6674 | ||
|
|
47c3b34612 | ||
|
|
d111fa0a3b | ||
|
|
66317c9848 | ||
|
|
eeb3c0e5c6 | ||
|
|
490f9f7dbc | ||
|
|
178b172c06 | ||
|
|
40cf82c2cb | ||
|
|
1ac2b592fb | ||
|
|
7870538ec9 | ||
|
|
e12883037c | ||
|
|
ee4e1e087f | ||
|
|
2d63fd63c1 | ||
|
|
7a8b5e835b | ||
|
|
e557e61a02 | ||
|
|
67da250615 | ||
|
|
77f7a773e3 | ||
|
|
cd013621a7 | ||
|
|
9a48a59f68 | ||
|
|
45712a04f1 | ||
|
|
535b8285e9 | ||
|
|
28a2950439 | ||
|
|
1e1d506697 | ||
|
|
013fff9f37 | ||
|
|
4edaf0813d | ||
|
|
2292ab813b | ||
|
|
a4ae5ed989 | ||
|
|
7e8508db53 | ||
|
|
c9f7b24e0f | ||
|
|
d850586a14 | ||
|
|
95605ebbd4 |
+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 {
|
||||
|
||||
+194
-34
@@ -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");
|
||||
@@ -536,11 +535,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||
|
||||
/* Do property inheritance */
|
||||
|
||||
/* NOTE: Need to do: Check properties for M > 1 and decrement
|
||||
* and repeat without moving CurrentProp
|
||||
*/
|
||||
|
||||
if (CurrentProp) {
|
||||
int i, mval;
|
||||
struct valuelist *kv;
|
||||
|
||||
for (ob2 = ChildStart; ob2 != NULL; ob2=ob2->next) {
|
||||
|
||||
/* If the parent cell has properties to declare, then */
|
||||
@@ -548,10 +546,49 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||
/* spiceparams dictionary is active (during file */
|
||||
/* reading only). */
|
||||
|
||||
if (ob2->type == PROPERTY)
|
||||
if (ob2->type == PROPERTY) {
|
||||
ReduceExpressions(ob2, CurrentProp, ChildCell,
|
||||
(spiceparams.hashtab == NULL) ? 0 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for property M. If it exists and is greater than */
|
||||
/* one, reduce it and repeat; i.e., generate multiple */
|
||||
/* child instances to match the M value. Probably this */
|
||||
/* could be done quicker by just creating a new property */
|
||||
/* M for a single child. */
|
||||
|
||||
mval = 0;
|
||||
for (i = 0; ; i++) {
|
||||
kv = &(CurrentProp->instance.props[i]);
|
||||
if (kv->type == PROP_ENDLIST) break;
|
||||
if ((*matchfunc)(kv->key, "M")) {
|
||||
if (kv->type == PROP_INTEGER) {
|
||||
mval = kv->value.ival;
|
||||
kv->value.ival = mval - 1;
|
||||
break;
|
||||
}
|
||||
else if (kv->type == PROP_DOUBLE) {
|
||||
mval = (int)kv->value.dval;
|
||||
kv->value.dval = (double)mval - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mval > 1) {
|
||||
/* Put the child cell at the start of ChildObjList */
|
||||
if (ChildEnd) {
|
||||
ChildEnd->next = ChildObjList;
|
||||
ChildObjList = ChildStart;
|
||||
|
||||
/* Continue without moving CurrentProp */
|
||||
/* Note that if ChildEnd is NULL then the child cell
|
||||
* is optimized out and there is no need to do it
|
||||
* M times.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Repeat for each property record, as each property represents a
|
||||
* unique instance that must be flattened individually.
|
||||
@@ -563,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 */
|
||||
@@ -1177,7 +1216,6 @@ int UniquePins(char *name, int filenum)
|
||||
firstport = (struct objlist **)CALLOC(maxnode + 1, sizeof(struct objlist *));
|
||||
|
||||
portcount = FIRSTPIN;
|
||||
lob = NULL;
|
||||
for (ob = ThisCell->cell; ob != NULL; ob = ob->next) {
|
||||
if (ob->type != PORT) break;
|
||||
if (ob->node > 0) {
|
||||
@@ -1188,16 +1226,16 @@ int UniquePins(char *name, int filenum)
|
||||
firstport[ob->node]->name, ThisCell->name, ThisCell->file);
|
||||
/* Do not count this as a duplicate pin. */
|
||||
nodecount[ob->node]--;
|
||||
/* Move the pin adjacent to the one it is shorted to (if it
|
||||
* isn't already); this will make the work of MatchPins() easier.
|
||||
/* Note: Previously there was code here to move the shorted port
|
||||
* next to the pin it is shorted to. This causes the cell def pins
|
||||
* to become scrambled with respect to the pin order of its instances.
|
||||
* Removed the code 9/1/2023. But---Not sure if any code depends
|
||||
* on shorted pins being adjacent.
|
||||
*/
|
||||
if (firstport[ob->node]->next != ob) {
|
||||
lob->next = ob->next;
|
||||
ob->next = firstport[ob->node]->next;
|
||||
firstport[ob->node]->next = ob;
|
||||
ob = lob;
|
||||
}
|
||||
lob = ob;
|
||||
/* When two pins are shorted, they are by definition permutable */
|
||||
PermuteSetup(ThisCell->name, ThisCell->file, ob->name,
|
||||
firstport[ob->node]->name);
|
||||
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
@@ -1218,7 +1256,6 @@ int UniquePins(char *name, int filenum)
|
||||
}
|
||||
}
|
||||
portcount++;
|
||||
lob = ob;
|
||||
}
|
||||
|
||||
if (needscleanup)
|
||||
@@ -1269,6 +1306,14 @@ int UniquePins(char *name, int filenum)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Structure used below for keeping track of node numbers
|
||||
* belonging to removed nodes.
|
||||
*/
|
||||
struct LinkedNum {
|
||||
int node;
|
||||
struct LinkedNum *next;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Callback function for CleanupPins */
|
||||
/* Note that if the first pin of the instance is a */
|
||||
@@ -1281,6 +1326,7 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
||||
struct nlist *ptr;
|
||||
struct objlist *ob, *obt, *lob, *nob, *firstpin, *pob;
|
||||
struct nlist *tc = (struct nlist *)clientdata;
|
||||
struct LinkedNum *newnodenum, *removedNodes = (struct LinkedNum *)NULL;
|
||||
int pinnum;
|
||||
char *saveinst = NULL;
|
||||
|
||||
@@ -1336,6 +1382,15 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
||||
saveinst = ob->instance.name;
|
||||
}
|
||||
if (ob->model.class != NULL) FREE(ob->model.class);
|
||||
|
||||
// Record the net number of the pin being removed, to
|
||||
// check at the end if the net belonged to a pin that
|
||||
// got orphaned.
|
||||
newnodenum = (struct LinkedNum *)MALLOC(sizeof(struct LinkedNum));
|
||||
newnodenum->node = ob->node;
|
||||
newnodenum->next = removedNodes;
|
||||
removedNodes = newnodenum;
|
||||
|
||||
FREE(ob);
|
||||
}
|
||||
else {
|
||||
@@ -1379,6 +1434,28 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
||||
}
|
||||
}
|
||||
|
||||
while (removedNodes != NULL) {
|
||||
int nodenum = removedNodes->node;
|
||||
struct objlist *ob2;
|
||||
|
||||
/* Only concerned with nodes that are in the pin list of ptr->cell */
|
||||
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
|
||||
if (ob->type != PORT) break;
|
||||
if (ob->node == nodenum) break;
|
||||
}
|
||||
if (ob && (ob->type == PORT)) {
|
||||
/* Check if this node number exists only in the port record */
|
||||
for (nob = ob->next; nob != NULL; nob = nob->next)
|
||||
if (nob->node == nodenum) break;
|
||||
if (nob == NULL) {
|
||||
ob->node = -1; /* This pin is now disconnected */
|
||||
}
|
||||
}
|
||||
newnodenum = removedNodes;
|
||||
removedNodes = removedNodes->next;
|
||||
FREE(newnodenum);
|
||||
}
|
||||
|
||||
if (saveinst != NULL) FREE(saveinst);
|
||||
return NULL; /* Keep the search going */
|
||||
}
|
||||
@@ -1599,7 +1676,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);
|
||||
@@ -1676,7 +1754,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))) {
|
||||
@@ -1684,7 +1762,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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1774,7 +1852,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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1840,7 +1918,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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1870,6 +1948,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) &&
|
||||
@@ -1916,6 +1996,33 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do NOT remove shorting devices that */
|
||||
/* connect two ports. Otherwise the */
|
||||
/* port lists get screwed up. It is */
|
||||
/* better in that case to force the */
|
||||
/* cells to be declared mismatched. */
|
||||
/* This is ignored for a top-level cell */
|
||||
/* because it will just show up as a */
|
||||
/* port mismatch error as it should. */
|
||||
|
||||
if ((not_top == TRUE) &&
|
||||
(ecomp->cell1->class != CLASS_ISOURCE)) {
|
||||
int found1 = FALSE;
|
||||
int found2 = FALSE;
|
||||
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
|
||||
if (!IsPort(ob2)) break;
|
||||
else if (ob2->node == node1)
|
||||
found1 = TRUE;
|
||||
else if (ob2->node == node2)
|
||||
found2 = TRUE;
|
||||
if (found1 && found2) {
|
||||
found = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) break;
|
||||
}
|
||||
if (found) {
|
||||
@@ -1929,6 +2036,16 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
|
||||
if (ecomp->cell1->class != CLASS_ISOURCE) {
|
||||
/* merge node of endpoints */
|
||||
/* Prefer a port node over a non-port node */
|
||||
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
|
||||
if (!IsPort(ob2)) break;
|
||||
else if (ob2->node == node1) break;
|
||||
else if (ob2->node == node2) {
|
||||
int ntemp = node1;
|
||||
node1 = node2;
|
||||
node2 = ntemp;
|
||||
}
|
||||
}
|
||||
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
|
||||
if (ob2->node == node2)
|
||||
ob2->node = node1;
|
||||
@@ -1955,7 +2072,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
|
||||
/* Remove from list */
|
||||
ecomp->num1--;
|
||||
modified++;
|
||||
modified1++;
|
||||
|
||||
ob1 = lob;
|
||||
}
|
||||
@@ -2027,6 +2144,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) {
|
||||
@@ -2037,6 +2175,16 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
|
||||
/* merge node of endpoints */
|
||||
if (ecomp->cell2->class != CLASS_ISOURCE) {
|
||||
/* Prefer a port node over a non-port node */
|
||||
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
|
||||
if (!IsPort(ob1)) break;
|
||||
else if (ob1->node == node1) break;
|
||||
else if (ob1->node == node2) {
|
||||
int ntemp = node1;
|
||||
node1 = node2;
|
||||
node2 = ntemp;
|
||||
}
|
||||
}
|
||||
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
|
||||
if (ob1->node == node2)
|
||||
ob1->node = node1;
|
||||
@@ -2062,8 +2210,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
}
|
||||
|
||||
/* Remove from list */
|
||||
ecomp->num1--;
|
||||
modified++;
|
||||
ecomp->num2--;
|
||||
modified2++;
|
||||
|
||||
ob2 = lob;
|
||||
}
|
||||
@@ -2097,7 +2245,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) {
|
||||
@@ -2129,7 +2277,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;
|
||||
}
|
||||
}
|
||||
@@ -2155,7 +2303,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;
|
||||
}
|
||||
}
|
||||
@@ -2186,5 +2334,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;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ along with this program; see the file copying. If not, write to
|
||||
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* inetcomp.c -- a simple wrapper to the NETCOMP() function */
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "netgen.h"
|
||||
|
||||
+1089
-398
File diff suppressed because it is too large
Load Diff
+6
-2
@@ -7,6 +7,7 @@ extern struct nlist *Circuit1;
|
||||
extern struct nlist *Circuit2;
|
||||
|
||||
extern int ExhaustiveSubdivision;
|
||||
extern int ExactTopology;
|
||||
|
||||
extern int left_col_end;
|
||||
extern int right_col_end;
|
||||
@@ -37,7 +38,8 @@ extern int PermuteSetup(char *model, int filenum, char *pin1, char *pin2);
|
||||
extern int PermuteForget(char *model, int filenum, char *pin1, char *pin2);
|
||||
extern int EquivalenceElements(char *name1, int file1, char *name2, int file2);
|
||||
extern int EquivalenceNodes(char *name1, int file1, char *name2, int file2);
|
||||
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2);
|
||||
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2,
|
||||
int dounique);
|
||||
extern int IgnoreClass(char *name, int file, unsigned char type);
|
||||
extern int MatchPins(struct nlist *tp1, struct nlist *tp2, int dolist);
|
||||
extern int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run,
|
||||
@@ -60,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);
|
||||
|
||||
@@ -68,6 +70,8 @@ extern int remove_group_tags(struct objlist *ob);
|
||||
#ifdef TCL_NETGEN
|
||||
extern int EquivalentNode();
|
||||
extern int EquivalentElement();
|
||||
extern void DeriveAreaProperty();
|
||||
extern void DerivePerimeterProperty();
|
||||
|
||||
extern void enable_interrupt();
|
||||
extern void disable_interrupt();
|
||||
|
||||
+68
-17
@@ -183,7 +183,6 @@ void CloseFile(char *filename)
|
||||
fclose(outfile);
|
||||
}
|
||||
|
||||
|
||||
/* STUFF TO READ INPUT FILES */
|
||||
|
||||
static char *line = NULL; /* actual line read in */
|
||||
@@ -207,6 +206,21 @@ struct hashdict *definitions = (struct hashdict *)NULL;
|
||||
|
||||
#define WHITESPACE_DELIMITER " \t\n\r"
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Seek and Tell on infile stream, for use with handling generate */
|
||||
/* loops in verilog. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void SeekFile(long offset)
|
||||
{
|
||||
fseek(infile, offset, SEEK_SET);
|
||||
}
|
||||
|
||||
long TellFile()
|
||||
{
|
||||
return ftell(infile);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* TrimQuoted() --- */
|
||||
/* Remove spaces from inside single- or double-quoted strings. */
|
||||
@@ -315,6 +329,11 @@ int GetNextLineNoNewline(char *delimiter)
|
||||
llen = strlen(line);
|
||||
}
|
||||
while (llen == linesize - 1) {
|
||||
/* Note that in the rare case where a newline is in the last buffer
|
||||
* position, we're done.
|
||||
*/
|
||||
if (*(line + llen - 1) == '\n') break;
|
||||
|
||||
newbuf = (char *)MALLOC(linesize + 501);
|
||||
strcpy(newbuf, line);
|
||||
FREE(line);
|
||||
@@ -573,19 +592,31 @@ 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)
|
||||
{
|
||||
int contline;
|
||||
|
||||
if ((nexttok = strdtok(NULL, WHITESPACE_DELIMITER, NULL)) != NULL) return;
|
||||
if ((nexttok = strdtok0(NULL, WHITESPACE_DELIMITER, NULL, FALSE)) != NULL) return;
|
||||
|
||||
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 != '+') {
|
||||
@@ -597,7 +628,8 @@ void SpiceTokNoNewline(void)
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Skip to the next token, ignoring any C-style comments. */
|
||||
/* Skip to the next token, ignoring any C-style comments and verilog */
|
||||
/* "(* ... *)"-style comments. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void SkipTokComments(char *delimiter)
|
||||
@@ -613,6 +645,11 @@ void SkipTokComments(char *delimiter)
|
||||
SkipTok(delimiter);
|
||||
if (nexttok) SkipTok(delimiter);
|
||||
}
|
||||
else if (match(nexttok, "(*")) {
|
||||
while (nexttok && !match(nexttok, "*)"))
|
||||
SkipTok(delimiter);
|
||||
if (nexttok) SkipTok(delimiter);
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
@@ -669,7 +706,7 @@ void SpiceSkipNewLine(void)
|
||||
/* the boundary between two-character and one-character delimiters. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
char *strdtok(char *pstring, char *delim1, char *delim2)
|
||||
char *strdtok0(char *pstring, char *delim1, char *delim2, char isverilog)
|
||||
{
|
||||
static char *stoken = NULL;
|
||||
static char *sstring = NULL;
|
||||
@@ -714,10 +751,10 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
|
||||
/* should know whether it is parsing SPICE or verilog and handle the syntax */
|
||||
/* accordingly (needs to be done). */
|
||||
|
||||
if (*s == '\\') {
|
||||
if (isverilog && (*s == '\\')) {
|
||||
s++;
|
||||
while (*s != '\0') {
|
||||
if ((*s == ' ') || (*s == '\\')) {
|
||||
if ((*s == ' ') || ((*s == '\\') && (*(s + 1) == '\0'))) {
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
@@ -785,6 +822,17 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
|
||||
return sstring;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* strdtok() is the original string tokenizer. It calls strdtok0() */
|
||||
/* with isverilog=TRUE, so that tokens are parsed as (potentially) */
|
||||
/* verilog names, which includes verilog backslash notation. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
char *strdtok(char *pstring, char *delim1, char *delim2)
|
||||
{
|
||||
return strdtok0(pstring, delim1, delim2, TRUE);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void InputParseError(FILE *f)
|
||||
@@ -866,7 +914,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;
|
||||
@@ -874,14 +922,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) */
|
||||
|
||||
@@ -890,6 +940,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"
|
||||
@@ -35,6 +36,7 @@ extern struct hashdict *definitions;
|
||||
|
||||
extern char *nexttok;
|
||||
#define SKIPTO(a) do {SkipTok(NULL);} while (!match(nexttok,a))
|
||||
extern char *strdtok0(char *pstring, char *delim1, char *delim2, char isverilog);
|
||||
extern char *strdtok(char *pstring, char *delim1, char *delim2);
|
||||
extern char *GetLineAtTok();
|
||||
extern void SkipTok(char *delimiter);
|
||||
@@ -47,5 +49,7 @@ extern void InputParseError(FILE *f);
|
||||
extern int OpenParseFile(char *name, int fnum);
|
||||
extern int EndParseFile(void);
|
||||
extern int CloseParseFile(void);
|
||||
extern void SeekFile(long offset); /* handles verilog 'for' loops */
|
||||
extern long TellFile(); /* handles verilog 'for' loops */
|
||||
|
||||
#endif /* _NETFILE_H */
|
||||
|
||||
+57
-5
@@ -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';
|
||||
@@ -984,6 +988,51 @@ PropertyDelete(char *name, int fnum, char *key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Associate a property with a specific pin */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
int
|
||||
PropertyAssociatePin(char *name, int fnum, char *key, char *pin)
|
||||
{
|
||||
struct property *kl = NULL;
|
||||
struct nlist *tc;
|
||||
struct objlist *ob;
|
||||
int result;
|
||||
|
||||
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
||||
result = PropertyAssociatePin(name, Circuit1->file, key, pin);
|
||||
result = PropertyAssociatePin(name, Circuit2->file, key, pin);
|
||||
return result;
|
||||
}
|
||||
|
||||
tc = LookupCellFile(name, fnum);
|
||||
if (tc == NULL) {
|
||||
Printf("No device %s found for PropertyAssociatePin()\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
kl = (struct property *)HashLookup(key, &(tc->propdict));
|
||||
if (kl == NULL) {
|
||||
Printf("No property %s found for device %s\n", key, name);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
for (ob = tc->cell; ob != NULL; ob = ob->next) {
|
||||
if (ob->type != PORT) break;
|
||||
else if ((*matchfunc)(ob->name, pin)) {
|
||||
kl->pin = ob->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ob == NULL) {
|
||||
Printf("No pin %s found for device %s\n", pin, name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Set the tolerance of a property in the master cell record. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -2199,7 +2248,10 @@ int PromoteProperty(struct property *prop, struct valuelist *vl,
|
||||
if (prop == NULL || vl == NULL) return -1;
|
||||
if (prop->type == vl->type) return 1; /* Nothing to do */
|
||||
result = 0;
|
||||
if (prop->type == PROP_EXPRESSION) {
|
||||
/* If vl is an expression but prop is not, then try to reduce
|
||||
* the expression in vl.
|
||||
*/
|
||||
if (vl->type == PROP_EXPRESSION) {
|
||||
ReduceOneExpression(vl, ob, tc, FALSE);
|
||||
}
|
||||
switch (prop->type) {
|
||||
@@ -3045,7 +3097,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) {
|
||||
@@ -3595,7 +3647,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);
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ extern void SetParallelCombine(int value);
|
||||
extern void SetSeriesCombine(int value);
|
||||
extern int PropertyTolerance(char *name, int fnum, char *key, int ival,
|
||||
double dval);
|
||||
extern int PropertyAssociatePin(char *name, int fnum, char *key, char *pin);
|
||||
extern int PropertyMerge(char *name, int fnum, char *key, int merge_type,
|
||||
int merge_mask);
|
||||
extern void ResolveProperties(char *name1, int file1, char *name2, int file2);
|
||||
@@ -190,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);
|
||||
|
||||
+121
-101
@@ -130,6 +130,7 @@ struct property *NewProperty(void)
|
||||
|
||||
kl = (struct property *)CALLOC(1,sizeof(struct property));
|
||||
if (kl == NULL) Fprintf(stderr,"NewProperty: Core allocation error\n");
|
||||
kl->pin = (char *)NULL;
|
||||
return (kl);
|
||||
}
|
||||
|
||||
@@ -222,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;
|
||||
}
|
||||
@@ -440,13 +464,6 @@ void RemoveShorted(char *class, int file)
|
||||
RecurseCellFileHashTable(removeshorted, file);
|
||||
}
|
||||
|
||||
/* Structure used to keep track of nodes needing checking */
|
||||
|
||||
struct linkednode {
|
||||
int node;
|
||||
struct linkednode *next;
|
||||
};
|
||||
|
||||
/* Remove instances of a deleted class from the database. */
|
||||
/* NOTE: This treats deleted classes as not existing, so it */
|
||||
/* needs to take care of disconnected ports in the same manner */
|
||||
@@ -457,13 +474,22 @@ struct linkednode {
|
||||
int deleteclass(struct hashlist *p, int file)
|
||||
{
|
||||
struct nlist *ptr;
|
||||
struct objlist *ob, *lob, *nob;
|
||||
struct linkednode *checknodes = NULL, *newlnode, *chknode;
|
||||
struct objlist *ob, *lob, *nob, *portnode;
|
||||
unsigned char *checknodes;
|
||||
int i;
|
||||
|
||||
ptr = (struct nlist *)(p->ptr);
|
||||
|
||||
if ((file != -1) && (ptr->file != file)) return 0;
|
||||
|
||||
/* Note: This could be made faster by enumerating all times each
|
||||
* node is used during the full pass, then subtracting each time
|
||||
* the node is deleted in a child, then disconnecting all nodes
|
||||
* that ended up with a zero count.
|
||||
*/
|
||||
checknodes = (unsigned char *)CALLOC(ptr->nodename_cache_maxnodenum + 1,
|
||||
sizeof(unsigned char));
|
||||
|
||||
lob = NULL;
|
||||
for (ob = ptr->cell; ob != NULL;) {
|
||||
nob = ob->next;
|
||||
@@ -471,12 +497,8 @@ int deleteclass(struct hashlist *p, int file)
|
||||
if ((*matchfunc)(ob->model.class, OldCell->name)) {
|
||||
HashDelete(ob->instance.name, &(ptr->instdict));
|
||||
while (1) {
|
||||
if (ob->type >= FIRSTPIN) {
|
||||
newlnode = (struct linkednode *)MALLOC(sizeof(struct linkednode));
|
||||
newlnode->node = ob->node;
|
||||
newlnode->next = checknodes;
|
||||
checknodes = newlnode;
|
||||
}
|
||||
if (ob->type >= FIRSTPIN)
|
||||
checknodes[ob->node] = (unsigned char)1;
|
||||
FreeObjectAndHash(ob, ptr);
|
||||
ob = nob;
|
||||
if (ob == NULL) break;
|
||||
@@ -499,26 +521,24 @@ int deleteclass(struct hashlist *p, int file)
|
||||
}
|
||||
}
|
||||
|
||||
while (checknodes != NULL) {
|
||||
struct objlist *portnode = NULL;
|
||||
|
||||
chknode = checknodes;
|
||||
checknodes = checknodes->next;
|
||||
|
||||
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
|
||||
if ((ob->type != PORT) && (portnode == NULL))
|
||||
break;
|
||||
else if ((ob->type == PORT) && (ob->node == chknode->node))
|
||||
portnode = ob;
|
||||
else if ((ob->type >= FIRSTPIN) && (ob->node == chknode->node))
|
||||
break;
|
||||
for (i = 0; i <= ptr->nodename_cache_maxnodenum; i++) {
|
||||
if (checknodes[i] != 0) {
|
||||
portnode = NULL;
|
||||
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
|
||||
if ((ob->type != PORT) && (portnode == NULL))
|
||||
break;
|
||||
else if ((ob->type == PORT) && (ob->node == i))
|
||||
portnode = ob;
|
||||
else if ((ob->type >= FIRSTPIN) && (ob->node == i))
|
||||
break;
|
||||
}
|
||||
if ((ob == NULL) && (portnode != NULL)) {
|
||||
/* Port became disconnected when child was deleted */
|
||||
portnode->node = -1;
|
||||
}
|
||||
}
|
||||
if ((ob == NULL) && (portnode != NULL)) {
|
||||
/* Port became disconnected when child was deleted */
|
||||
portnode->node = -1;
|
||||
}
|
||||
FREE(chknode);
|
||||
}
|
||||
FREE(checknodes);
|
||||
}
|
||||
|
||||
/* Remove all instances of class "class" from the database */
|
||||
@@ -599,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. */
|
||||
@@ -666,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 */
|
||||
@@ -732,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 *******************************/
|
||||
|
||||
+11
-9
@@ -135,6 +135,7 @@ struct property {
|
||||
unsigned char idx; /* index into valuelist */
|
||||
unsigned char type; /* string, integer, double, value, expression */
|
||||
unsigned char merge; /* how property changes when devices are merged */
|
||||
char *pin; /* associated pin (or NULL if not associated) */
|
||||
union {
|
||||
char *string;
|
||||
double dval;
|
||||
@@ -206,7 +207,7 @@ struct nlist {
|
||||
char *name;
|
||||
int number; /* number of instances defined */
|
||||
int dumped; /* instance count, and general-purpose marker */
|
||||
unsigned char flags;
|
||||
unsigned short flags;
|
||||
unsigned char class;
|
||||
unsigned long classhash; /* randomized hash value for cell class */
|
||||
struct Permutation *permutes; /* list of permuting pins */
|
||||
@@ -222,17 +223,18 @@ struct nlist {
|
||||
|
||||
/* Defined nlist structure flags */
|
||||
|
||||
#define CELL_MATCHED 0x01 /* cell matched to another */
|
||||
#define CELL_NOCASE 0x02 /* cell is case-insensitive (e.g., SPICE) */
|
||||
#define CELL_TOP 0x04 /* cell is a top-level cell */
|
||||
#define CELL_PLACEHOLDER 0x08 /* cell is a placeholder cell */
|
||||
#define CELL_PROPSMATCHED 0x10 /* properties matched to matching cell */
|
||||
#define CELL_DUPLICATE 0x20 /* cell has a duplicate */
|
||||
#define CELL_MATCHED 0x001 /* cell matched to another */
|
||||
#define CELL_NOCASE 0x002 /* cell is case-insensitive (e.g., SPICE) */
|
||||
#define CELL_TOP 0x004 /* cell is a top-level cell */
|
||||
#define CELL_PLACEHOLDER 0x008 /* cell is a placeholder cell */
|
||||
#define CELL_PROPSMATCHED 0x010 /* properties matched to matching cell */
|
||||
#define CELL_DUPLICATE 0x020 /* cell has a duplicate */
|
||||
#define CELL_VERILOG 0x040 /* cell is verilog module */
|
||||
|
||||
/* Flags for combination allowances and prohibitions */
|
||||
|
||||
#define COMB_SERIES 0x40
|
||||
#define COMB_NO_PARALLEL 0x80
|
||||
#define COMB_SERIES 0x100
|
||||
#define COMB_NO_PARALLEL 0x200
|
||||
|
||||
extern struct nlist *CurrentCell;
|
||||
extern struct objlist *CurrentTail;
|
||||
|
||||
+98
-8
@@ -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';
|
||||
@@ -531,8 +532,14 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
|
||||
SkipTok(NULL); /* get the next token */
|
||||
if ((EndParseFile()) && (nexttok == NULL)) break;
|
||||
if (nexttok == NULL) break;
|
||||
|
||||
if (nexttok[0] == '*') SkipNewLine(NULL);
|
||||
/* Handle comment lines. Note that some variants of CDL format
|
||||
* use "*." for information that is transparent to SPICE simulators.
|
||||
* Handle "*.GLOBAL" entries. All others are ignored.
|
||||
*/
|
||||
if ((nexttok[0] == '*') && (!matchnocase(nexttok, "*.GLOBAL")))
|
||||
SkipNewLine(NULL);
|
||||
|
||||
else if (matchnocase(nexttok, ".SUBCKT")) {
|
||||
SpiceTokNoNewline();
|
||||
@@ -557,6 +564,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 +603,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 +734,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;
|
||||
@@ -752,7 +827,10 @@ skip_ends:
|
||||
|
||||
// Handle some commonly-used cards
|
||||
|
||||
else if (matchnocase(nexttok, ".GLOBAL")) {
|
||||
/* .GLOBAL and *.GLOBAL. Note that *.GLOBAL is excepted from comment-line
|
||||
* handling, above, so any line starting with '*' is "*.GLOBAL".
|
||||
*/
|
||||
else if (matchnocase(nexttok, ".GLOBAL") || (nexttok[0] == '*')) {
|
||||
while (nexttok != NULL) {
|
||||
int numnodes = 0;
|
||||
SpiceTokNoNewline();
|
||||
@@ -1783,6 +1861,18 @@ skip_ends:
|
||||
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
||||
update = 1;
|
||||
}
|
||||
else if (tp->flags & CELL_VERILOG) {
|
||||
if (tp->flags & CELL_PLACEHOLDER) {
|
||||
/* Flag this as an error. To do: Rearrange the verilog instance pins to */
|
||||
/* match the SPICE subcircuit pin order. */
|
||||
Fprintf(stderr, "Error: SPICE subcircuit %s should be read before verilog "
|
||||
"module using it, or pins may not match!\n", subcktname);
|
||||
}
|
||||
else {
|
||||
Fprintf(stderr, "Error: SPICE subcircuit %s redefines a verilog module!\n",
|
||||
subcktname);
|
||||
}
|
||||
}
|
||||
|
||||
/* nexttok is now NULL, scan->name points to class */
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
#include "print.h"
|
||||
#endif
|
||||
|
||||
extern void Finsert(FILE *f);
|
||||
|
||||
void test_entry(void)
|
||||
{
|
||||
|
||||
|
||||
+544
-41
@@ -42,6 +42,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* for calloc(), free(), getenv() */
|
||||
#include <ctype.h> /* for isalnum() */
|
||||
#ifndef IBMPC
|
||||
#include <sys/types.h> /* for getpwnam() tilde expansion */
|
||||
#include <pwd.h>
|
||||
@@ -56,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.
|
||||
@@ -63,6 +65,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
#define VLOG_EQUATION_DELIMITERS "X///**/#((**)X,;:(){}[]=+-*/"
|
||||
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
|
||||
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
|
||||
#define VLOG_INTEGER_DELIMITERS "X///**/X;[]"
|
||||
|
||||
// Used by portelement structure "flags" record.
|
||||
#define PORT_NOT_FOUND 0
|
||||
@@ -83,6 +86,16 @@ struct bus {
|
||||
int end;
|
||||
};
|
||||
|
||||
// Global storage for a 'for' loop
|
||||
struct _loop {
|
||||
char *loopvar;
|
||||
int start;
|
||||
int end;
|
||||
long filepos;
|
||||
};
|
||||
|
||||
struct _loop loop;
|
||||
|
||||
// Free a bus structure in the hash table during cleanup
|
||||
|
||||
int freebus (struct hashlist *p)
|
||||
@@ -244,7 +257,16 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
|
||||
|
||||
for (texp = start; texp; texp = texp->next) {
|
||||
if ((texp->last != NULL) && (texp->next != NULL) && (texp->oper != '\0')) {
|
||||
if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
|
||||
/* Watch for (a)*b+c or a+b*(c); multiplies must be solved first */
|
||||
if ((texp->last->last != NULL) && ((texp->last->last->oper == '*')
|
||||
|| (texp->last->last->oper == '/'))) {
|
||||
/* Do nothing */
|
||||
}
|
||||
else if ((texp->next->next != NULL) && ((texp->next->next->oper == '*')
|
||||
|| (texp->next->next->oper == '/'))) {
|
||||
/* Do nothing */
|
||||
}
|
||||
else if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
|
||||
if (texp->oper == '-') {
|
||||
/* Subtract */
|
||||
texp->last->value -= texp->next->value;
|
||||
@@ -290,6 +312,7 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If only one numerical item remains, then place it in valptr and return 1 */
|
||||
@@ -311,6 +334,134 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Parse an expression which must resolve to a single integer.
|
||||
// If it does, return "1" and put the integer in the return
|
||||
// pointer "iptr". If not, return "0" and iptr is undefined.
|
||||
// The expression may use parameters, standard arithmetic,
|
||||
// and parenthetic grouping. This routine does not tokenize input
|
||||
// but assumes that the entire expression is in the string "expr".
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
int ParseIntegerExpression(char *expr, int *iptr)
|
||||
{
|
||||
int result, value;
|
||||
char *sptr, *cptr, savec;
|
||||
struct property *kl = NULL;
|
||||
struct expr_stack *stack, *newexp;
|
||||
|
||||
stack = NULL;
|
||||
savec = '\0';
|
||||
sptr = expr;
|
||||
result = 1;
|
||||
|
||||
while (sptr && (*sptr != '\0')) {
|
||||
// Move sptr to first non-space character
|
||||
while (isspace(*sptr)) sptr++;
|
||||
|
||||
// Tokenize. Look ahead to next delimeter and truncate string there.
|
||||
cptr = sptr + 1;
|
||||
if (isalnum(*sptr) || (*sptr == '_') || (*sptr == '$')) {
|
||||
while (*cptr != '\0') {
|
||||
if (isalnum(*cptr)) cptr++;
|
||||
else if ((*cptr == '_') || (*cptr == '$')) cptr++;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
savec = *cptr;
|
||||
*cptr = '\0';
|
||||
|
||||
if (match(sptr, "+") || match(sptr, "-")
|
||||
|| match(sptr, "*") || match(sptr, "/")
|
||||
|| match(sptr, "(") || match(sptr, ")")) {
|
||||
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
|
||||
newexp->oper = *sptr;
|
||||
newexp->value = 0;
|
||||
newexp->next = NULL;
|
||||
newexp->last = stack;
|
||||
if (stack) stack->next = newexp;
|
||||
stack = newexp;
|
||||
|
||||
sptr = cptr;
|
||||
*cptr = savec;
|
||||
continue;
|
||||
}
|
||||
if ((result = sscanf(sptr, "%d", &value)) != 1) {
|
||||
|
||||
// Is name in the parameter list?
|
||||
kl = (struct property *)HashLookup(nexttok, &verilogparams);
|
||||
if (kl == NULL) {
|
||||
Printf("Value %s in expression is not a number or a parameter.\n",
|
||||
sptr);
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (kl->type == PROP_STRING) {
|
||||
result = sscanf(kl->pdefault.string, "%d", &value);
|
||||
if (result != 1) {
|
||||
Printf("Parameter %s has value %s that cannot be parsed"
|
||||
" as an integer.\n",
|
||||
nexttok, kl->pdefault.string);
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (kl->type == PROP_INTEGER) {
|
||||
value = kl->pdefault.ival;
|
||||
}
|
||||
else if (kl->type == PROP_DOUBLE) {
|
||||
value = (int)kl->pdefault.dval;
|
||||
if ((double)value != kl->pdefault.dval) {
|
||||
Printf("Parameter %s has value %g that cannot be parsed"
|
||||
" as an integer.\n",
|
||||
nexttok, kl->pdefault.dval);
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Printf("Parameter %s has unknown type; don't know how"
|
||||
" to parse.\n", nexttok);
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
|
||||
newexp->oper = '\0';
|
||||
newexp->value = value;
|
||||
newexp->next = NULL;
|
||||
newexp->last = stack;
|
||||
if (stack) stack->next = newexp;
|
||||
stack = newexp;
|
||||
|
||||
/* Move to next token */
|
||||
sptr = cptr;
|
||||
*cptr = savec;
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
if (stack == NULL) {
|
||||
Printf("Empty array found.\n");
|
||||
result = 0;
|
||||
}
|
||||
else if (EvalExpr(&stack, iptr) != 1) {
|
||||
Printf("Bad expression found in array.\n");
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of error, stack may need cleaning up */
|
||||
while (stack != NULL) {
|
||||
newexp = stack;
|
||||
stack = stack->last;
|
||||
FREE(newexp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Get bus indexes from the notation name[a:b]. If there is only "name"
|
||||
// then look up the name in the bus hash list and return the index bounds.
|
||||
@@ -323,7 +474,6 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
|
||||
int GetBusTok(struct bus *wb)
|
||||
{
|
||||
int result, start, end, value;
|
||||
char oper;
|
||||
struct property *kl = NULL;
|
||||
struct expr_stack *stack, *newexp;
|
||||
|
||||
@@ -338,7 +488,6 @@ int GetBusTok(struct bus *wb)
|
||||
|
||||
if (match(nexttok, "[")) {
|
||||
start = end = -1;
|
||||
oper = '\0';
|
||||
while (nexttok) {
|
||||
SkipTokComments(VLOG_EQUATION_DELIMITERS);
|
||||
if (match(nexttok, "]")) {
|
||||
@@ -467,13 +616,16 @@ int GetBusTok(struct bus *wb)
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// GetBus() is similar to GetBusTok() (see above), but it parses from
|
||||
// a string instead of the input tokenizer.
|
||||
// a string instead of the input tokenizer, and expressions are not
|
||||
// allowed.
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
int GetBus(char *astr, struct bus *wb)
|
||||
{
|
||||
char *colonptr, *brackstart, *brackend, *sigend, sdelim, *aastr;
|
||||
int result, start, end;
|
||||
struct property *kl = NULL;
|
||||
struct expr_stack *stack, *newexp;
|
||||
|
||||
if (wb == NULL) return 0;
|
||||
else {
|
||||
@@ -531,7 +683,7 @@ int GetBus(char *astr, struct bus *wb)
|
||||
*brackend = '\0';
|
||||
colonptr = strvchr(aastr, ':');
|
||||
if (colonptr) *colonptr = '\0';
|
||||
result = sscanf(brackstart + 1, "%d", &start);
|
||||
result = ParseIntegerExpression(brackstart + 1, &start);
|
||||
if (colonptr) *colonptr = ':';
|
||||
if (result != 1) {
|
||||
Printf("Badly formed array notation \"%s\"\n", astr);
|
||||
@@ -539,7 +691,7 @@ int GetBus(char *astr, struct bus *wb)
|
||||
return 1;
|
||||
}
|
||||
if (colonptr)
|
||||
result = sscanf(colonptr + 1, "%d", &end);
|
||||
result = ParseIntegerExpression(colonptr + 1, &end);
|
||||
else {
|
||||
result = 1;
|
||||
end = start; // Single bit
|
||||
@@ -565,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)
|
||||
{
|
||||
@@ -803,6 +957,56 @@ extern void IncludeVerilog(char *, int, struct cellstack **, int);
|
||||
extern void PushStack(char *cellname, struct cellstack **top);
|
||||
extern void PopStack(struct cellstack **top);
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Callback routine for FindInstanceOf() */
|
||||
/* NOTE: This casts a (struct objlist) pointer to a */
|
||||
/* (struct nlist) pointer for the purpose of using */
|
||||
/* RecurseCellHashTable2(). FindInstanceOf() casts it */
|
||||
/* back into a (struct objlist) pointer. */
|
||||
/*------------------------------------------------------*/
|
||||
|
||||
struct nlist *findInstance(struct hashlist *p, void *clientdata)
|
||||
{
|
||||
struct nlist *ptr;
|
||||
struct objlist *ob;
|
||||
struct nlist *tref = (struct nlist *)clientdata;
|
||||
|
||||
ptr = (struct nlist *)(p->ptr);
|
||||
if (ptr->file != tref->file) return NULL;
|
||||
|
||||
ob = LookupInstance(tref->name, ptr);
|
||||
return (struct nlist *)ob;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Routine to find the first instance of a cell */
|
||||
/*------------------------------------------------------*/
|
||||
|
||||
struct objlist *FindInstanceOf(struct nlist *tc)
|
||||
{
|
||||
return (struct objlist *)RecurseCellHashTable2(findInstance, (void *)tc);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Given a reference cell pointer tref and a port name */
|
||||
/* portname, check if portname is a port of tref. If */
|
||||
/* not, then call Port() to add one. If tref is NULL, */
|
||||
/* then always add the port. */
|
||||
/*------------------------------------------------------*/
|
||||
|
||||
void CheckPort(struct objlist *tref, char *portname)
|
||||
{
|
||||
struct objlist *ob;
|
||||
|
||||
if (tref != NULL) {
|
||||
for (ob = CurrentCell->cell; ob && (ob->type == PORT); ob = ob->next) {
|
||||
if ((*matchfunc)(ob->name, portname))
|
||||
return;
|
||||
}
|
||||
}
|
||||
Port(portname);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Read a verilog structural netlist */
|
||||
/*------------------------------------------------------*/
|
||||
@@ -816,16 +1020,17 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
char devtype, in_module, in_param;
|
||||
char *eqptr, *matchptr;
|
||||
struct keyvalue *kvlist = NULL;
|
||||
char inst[MAX_STR_LEN], model[MAX_STR_LEN], instname[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN];
|
||||
struct nlist *tp;
|
||||
struct objlist *parent, *sobj, *nobj, *lobj, *pobj;
|
||||
char inst[MAX_STR_LEN], model[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN];
|
||||
struct nlist *tp, *tpsave;
|
||||
struct objlist *parent, *sobj, *nobj, *lobj, *pobj, *cref;
|
||||
|
||||
inst[MAX_STR_LEN-1] = '\0';
|
||||
model[MAX_STR_LEN-1] = '\0';
|
||||
instname[MAX_STR_LEN-1] = '\0';
|
||||
in_module = (char)0;
|
||||
in_param = (char)0;
|
||||
|
||||
loop.loopvar = NULL;
|
||||
|
||||
while (!EndParseFile()) {
|
||||
|
||||
SkipTokComments(VLOG_DELIMITERS); /* get the next token */
|
||||
@@ -938,6 +1143,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
InputParseError(stderr);
|
||||
}
|
||||
in_module = (char)1;
|
||||
cref = NULL;
|
||||
|
||||
/* Save pointer to current cell */
|
||||
if (CurrentCell != NULL)
|
||||
@@ -949,6 +1155,8 @@ 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 */
|
||||
/* This may mean that the cell was used before it was */
|
||||
@@ -986,23 +1194,22 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
CellDef(nexttok, filenum);
|
||||
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);
|
||||
else if (tp != NULL) { /* Cell exists, but as a placeholder */
|
||||
tpsave = tp;
|
||||
CellDef("_PLACEHOLDER_", filenum);
|
||||
tp = LookupCellFile("_PLACEHOLDER_", filenum);
|
||||
}
|
||||
else if (tp == NULL) { /* Completely new cell, no name conflict */
|
||||
CellDef(model, filenum);
|
||||
tp = LookupCellFile(model, filenum);
|
||||
}
|
||||
|
||||
hasports = (char)0;
|
||||
inlined_decls = (char)0;
|
||||
|
||||
if (tp != NULL) {
|
||||
struct bus wb, *nb;
|
||||
|
||||
tp->flags |= CELL_VERILOG;
|
||||
PushStack(tp->name, CellStackPtr);
|
||||
|
||||
/* Need to support both types of I/O lists: Those */
|
||||
@@ -1071,7 +1278,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
if (GetBusTok(&wb) != 0) {
|
||||
// Didn't parse as a bus, so wing it
|
||||
wb.start = wb.end = -1;
|
||||
Port(nexttok);
|
||||
CheckPort(cref, nexttok);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1079,13 +1286,13 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
if (wb.start > wb.end) {
|
||||
for (i = wb.start; i >= wb.end; i--) {
|
||||
sprintf(portname, "%s[%d]", nexttok, i);
|
||||
Port(portname);
|
||||
CheckPort(cref, portname);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = wb.start; i <= wb.end; i++) {
|
||||
sprintf(portname, "%s[%d]", nexttok, i);
|
||||
Port(portname);
|
||||
CheckPort(cref, portname);
|
||||
}
|
||||
}
|
||||
/* Also register this port as a bus */
|
||||
@@ -1097,7 +1304,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
wb.start = wb.end = -1;
|
||||
}
|
||||
else {
|
||||
Port(nexttok);
|
||||
CheckPort(cref, nexttok);
|
||||
}
|
||||
}
|
||||
hasports = 1;
|
||||
@@ -1153,7 +1360,7 @@ skip_endmodule:
|
||||
if (GetBusTok(&wb) != 0) {
|
||||
// Didn't parse as a bus, so wing it
|
||||
wb.start = wb.end = -1;
|
||||
Port(nexttok);
|
||||
CheckPort(cref, nexttok);
|
||||
}
|
||||
}
|
||||
else if (!match(nexttok, ",")) {
|
||||
@@ -1161,13 +1368,13 @@ skip_endmodule:
|
||||
if (wb.start > wb.end) {
|
||||
for (i = wb.start; i >= wb.end; i--) {
|
||||
sprintf(portname, "%s[%d]", nexttok, i);
|
||||
Port(portname);
|
||||
CheckPort(cref, portname);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = wb.start; i <= wb.end; i++) {
|
||||
sprintf(portname, "%s[%d]", nexttok, i);
|
||||
Port(portname);
|
||||
CheckPort(cref, portname);
|
||||
}
|
||||
}
|
||||
/* Also register this port as a bus */
|
||||
@@ -1178,7 +1385,7 @@ skip_endmodule:
|
||||
wb.start = wb.end = -1;
|
||||
}
|
||||
else {
|
||||
Port(nexttok);
|
||||
CheckPort(cref, nexttok);
|
||||
}
|
||||
}
|
||||
hasports = 1;
|
||||
@@ -1194,10 +1401,98 @@ skip_endmodule:
|
||||
InputParseError(stderr);
|
||||
}
|
||||
in_module = (char)0;
|
||||
cref = NULL;
|
||||
|
||||
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")) {
|
||||
@@ -1338,6 +1633,145 @@ skip_endmodule:
|
||||
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
|
||||
continue;
|
||||
}
|
||||
else if (match(nexttok, "end")) {
|
||||
/* Handle a 'for' loop */
|
||||
if (loop.loopvar != NULL) {
|
||||
int loopval;
|
||||
struct property *klr;
|
||||
|
||||
klr = (struct property *)HashLookup(loop.loopvar, &verilogparams);
|
||||
loopval = klr->pdefault.ival;
|
||||
|
||||
if (loopval < loop.end) loopval++;
|
||||
else if (loopval > loop.end) loopval--;
|
||||
SeekFile(loop.filepos);
|
||||
if (loopval == loop.end)
|
||||
{
|
||||
HashDelete(loop.loopvar, &verilogparams);
|
||||
FREE(klr);
|
||||
FREE(loop.loopvar);
|
||||
loop.loopvar = NULL;
|
||||
}
|
||||
else
|
||||
klr->pdefault.ival = loopval;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (match(nexttok, "begin") || match(nexttok, "generate")) {
|
||||
/* 'generate' section or 'for' loop start is ignored */
|
||||
continue;
|
||||
}
|
||||
else if (match(nexttok, "generate") || match(nexttok, "endgenerate")) {
|
||||
/* 'generate' section is ignored */
|
||||
continue;
|
||||
}
|
||||
else if (match(nexttok, "genvar")) {
|
||||
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
|
||||
continue;
|
||||
}
|
||||
else if (match(nexttok, "for")) {
|
||||
char limittype = '\0';
|
||||
char *paramkey = NULL;
|
||||
struct property *kl = NULL, *klr;
|
||||
/* Parse out the loop variable and count. Set the loop
|
||||
* variable as a parameter. Find the beginning of the
|
||||
* loop block and record the file position so that the
|
||||
* block can be re-parsed for each loop iteration.
|
||||
*/
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
if ((nexttok == NULL) || !match(nexttok, "(")) {
|
||||
Printf("Badly formed 'for' loop.\n");
|
||||
FREE(paramkey);
|
||||
break;
|
||||
}
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
/* Next token must be the loop variable */
|
||||
if (nexttok == NULL) break;
|
||||
paramkey = strsave(nexttok);
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
if (!match(nexttok, "=")) {
|
||||
Printf("Badly formed 'for' loop.\n");
|
||||
FREE(paramkey);
|
||||
break;
|
||||
}
|
||||
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
|
||||
if (ParseIntegerExpression(nexttok, &ival) == 0) {
|
||||
FREE(paramkey);
|
||||
break;
|
||||
}
|
||||
|
||||
kl = NewProperty();
|
||||
kl->type = PROP_INTEGER;
|
||||
kl->slop.ival = 0;
|
||||
kl->pdefault.ival = ival;
|
||||
kl->key = paramkey;
|
||||
kl->idx = 0;
|
||||
kl->merge = MERGE_NONE;
|
||||
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
if (!match(nexttok, ";")) {
|
||||
Printf("Badly formed 'for' loop.\n");
|
||||
FREE(paramkey);
|
||||
FREE(kl);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Assuming a standard 'for' loop here */
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
if (!match(nexttok, paramkey)) {
|
||||
Printf("Don't know how to parse this 'for' loop!\n");
|
||||
FREE(paramkey);
|
||||
FREE(kl);
|
||||
break;
|
||||
}
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
if (strlen(nexttok) == 1) limittype = *nexttok;
|
||||
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
|
||||
if (ParseIntegerExpression(nexttok, &ival) == 0) {
|
||||
FREE(kl);
|
||||
FREE(paramkey);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Loops will stop after the last value, so if value is > or <,
|
||||
* then adjust the end value accordingly.
|
||||
*/
|
||||
if (limittype == '<')
|
||||
ival--;
|
||||
else if (limittype == '>')
|
||||
ival++;
|
||||
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
if (!match(nexttok, ";")) {
|
||||
Printf("Badly formed 'for' loop.\n");
|
||||
FREE(paramkey);
|
||||
FREE(kl);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Assume a standard 'for' loop and skip to the block begin */
|
||||
/* To do: Parse out the loop increment value. */
|
||||
while (1) {
|
||||
SkipTokNoNewline(VLOG_DELIMITERS);
|
||||
if (EndParseFile()) break;
|
||||
else if (match(nexttok, "begin")) break;
|
||||
}
|
||||
if (EndParseFile()) {
|
||||
Printf("Badly formed 'for' loop: No begin/end block.\n");
|
||||
FREE(paramkey);
|
||||
FREE(kl);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save the loop variable, ending value, and file position */
|
||||
loop.loopvar = paramkey;
|
||||
loop.start = kl->pdefault.ival;
|
||||
loop.end = ival;
|
||||
loop.filepos = TellFile();
|
||||
|
||||
/* 'for' loop has been completely parsed, so save the loop variable */
|
||||
HashPtrInstall(paramkey, kl, &verilogparams);
|
||||
}
|
||||
else if (match(nexttok, "wire") || match(nexttok, "assign")) { /* wire = node */
|
||||
struct bus wb, wb2, *nb;
|
||||
char nodename[MAX_STR_LEN], noderoot[MAX_STR_LEN];
|
||||
@@ -1401,8 +1835,9 @@ skip_endmodule:
|
||||
}
|
||||
else { /* "assign" */
|
||||
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
||||
if (GetBus(nexttok, &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 */
|
||||
@@ -1420,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);
|
||||
@@ -1436,9 +1880,11 @@ skip_endmodule:
|
||||
// "assign a = b" joins two nets.
|
||||
// "assign a = {b, c, ...}" creates a bus from components.
|
||||
// "assign" using any boolean arithmetic is not structural verilog.
|
||||
// "assign a = {x{b}}" creates a bus by repeating a component.
|
||||
|
||||
if (nexttok && match(nexttok, "=")) {
|
||||
char assignname[MAX_STR_LEN], assignroot[MAX_STR_LEN];
|
||||
int multiplier = 1;
|
||||
|
||||
i = wb.start;
|
||||
while (1) {
|
||||
@@ -1447,6 +1893,10 @@ skip_endmodule:
|
||||
|
||||
if (match(nexttok, "{")) {
|
||||
/* RHS is a bundle */
|
||||
/* Make provisional multiplier active. NOTE: */
|
||||
/* this is not going to properly handle complex */
|
||||
/* multipliers like {2{a,b}}. Needs rework. */
|
||||
if (multiplier < 0) multiplier = -multiplier;
|
||||
continue;
|
||||
}
|
||||
else if (match(nexttok, "}")) {
|
||||
@@ -1462,8 +1912,9 @@ skip_endmodule:
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (GetBus(nexttok, &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';
|
||||
@@ -1478,6 +1929,18 @@ skip_endmodule:
|
||||
else {
|
||||
j = -1;
|
||||
rhs = LookupObject(nexttok, CurrentCell);
|
||||
|
||||
/* Check if rhs starts with a signal multiplier */
|
||||
if (rhs == NULL) {
|
||||
if (ConvertStringToInteger(nexttok, &multiplier) == 1) {
|
||||
/* Set multiplier to a negative value to
|
||||
* indicate that it is provisional, waiting on
|
||||
* signal specification to follow.
|
||||
*/
|
||||
multiplier = -multiplier;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((lhs == NULL) || (rhs == NULL)) {
|
||||
if (rhs != NULL) {
|
||||
@@ -1490,7 +1953,7 @@ skip_endmodule:
|
||||
Printf("Improper expression is \"%s\".\n", nexttok);
|
||||
break;
|
||||
}
|
||||
if (lhs != NULL) {
|
||||
if ((lhs != NULL) || (multiplier < 0)) {
|
||||
Printf("Improper assignment; right-hand side cannot "
|
||||
"be parsed.\n");
|
||||
if (i != -1)
|
||||
@@ -1510,21 +1973,36 @@ skip_endmodule:
|
||||
/* until bits in signal are exhausted or LHS is full. */
|
||||
|
||||
if (i != -1)
|
||||
sprintf(nodename, "%s[%d]", noderoot, i);
|
||||
else
|
||||
sprintf(nodename, lhs->name);
|
||||
snprintf(nodename, MAX_STR_LEN, "%s[%d]", noderoot, i);
|
||||
else if (lhs != NULL)
|
||||
strncpy(nodename, lhs->name, MAX_STR_LEN - 1);
|
||||
else {
|
||||
Printf("Error: Improper node name \"%s\".\n", noderoot);
|
||||
goto skip_endmodule;
|
||||
}
|
||||
if (j != -1)
|
||||
sprintf(assignname, "%s[%d]", assignroot, j);
|
||||
else
|
||||
sprintf(assignname, rhs->name);
|
||||
snprintf(assignname, MAX_STR_LEN, "%s[%d]", assignroot, j);
|
||||
else if (rhs != NULL)
|
||||
strncpy(assignname, rhs->name, MAX_STR_LEN - 1);
|
||||
else {
|
||||
Printf("Error: Improper assignment name \"%s\".\n",
|
||||
assignroot);
|
||||
goto skip_endmodule;
|
||||
}
|
||||
|
||||
join(nodename, assignname);
|
||||
|
||||
if (i == wb.end) break;
|
||||
i += (wb.end > wb.start) ? 1 : -1;
|
||||
|
||||
if (j == wb2.end) break;
|
||||
j += (wb2.end > wb2.start) ? 1 : -1;
|
||||
if (j == wb2.end) {
|
||||
if (multiplier <= 1)
|
||||
break;
|
||||
else
|
||||
multiplier--;
|
||||
}
|
||||
else
|
||||
j += (wb2.end > wb2.start) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1732,6 +2210,31 @@ nextinst:
|
||||
}
|
||||
new_port->net = wire_bundle;
|
||||
}
|
||||
else if ((strchr(nexttok, '[') != NULL) &&
|
||||
(strchr(nexttok, ']') == NULL)) {
|
||||
/* If a bus expressions has whitespace, then treat it like
|
||||
* a bundle, above, concatenating to the closing ']'.
|
||||
*/
|
||||
char *array_expr = (char *)MALLOC(1);
|
||||
char *new_array_expr = NULL;
|
||||
*array_expr = '\0';
|
||||
/* Read to "]" */
|
||||
while (nexttok) {
|
||||
new_array_expr = (char *)MALLOC(strlen(array_expr) +
|
||||
strlen(nexttok) + 1);
|
||||
/* Roundabout way to do realloc() becase there is no REALLOC() */
|
||||
strcpy(new_array_expr, array_expr);
|
||||
strcat(new_array_expr, nexttok);
|
||||
FREE(array_expr);
|
||||
array_expr = new_array_expr;
|
||||
if (strchr(nexttok, ']')) break;
|
||||
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
||||
}
|
||||
if (!nexttok) {
|
||||
Printf("Unterminated net in pin %s\n", array_expr);
|
||||
}
|
||||
new_port->net = array_expr;
|
||||
}
|
||||
else if (nexttok[0] == '~' || nexttok[0] == '!' || nexttok[0] == '-') {
|
||||
/* All of these imply that the signal is logically manipulated */
|
||||
/* in turn implying behavioral code. */
|
||||
@@ -2115,7 +2618,7 @@ nextinst:
|
||||
sprintf(localnet, "_noconnect_%d_", localcount++);
|
||||
Node(localnet);
|
||||
join(localnet, obptr->name);
|
||||
Fprintf(stderr,
|
||||
Fprintf(stdout,
|
||||
"Note: Implicit pin %s in instance %s of %s in cell %s\n",
|
||||
obpinname, locinst, modulename, CurrentCell->name);
|
||||
}
|
||||
@@ -2207,8 +2710,8 @@ nextinst:
|
||||
char tempname[MAX_STR_LEN];
|
||||
int maxnode;
|
||||
|
||||
/* This pin was probably implicit in the first call */
|
||||
/* and so it needs to be added to the definition. */
|
||||
/* This pin was probably implicit in the first call */
|
||||
/* and so it needs to be added to the definition. */
|
||||
|
||||
ReopenCellDef(modulename, filenum);
|
||||
Port(scan->name);
|
||||
@@ -2265,7 +2768,7 @@ nextinst:
|
||||
sprintf(tempname, "_noconnect_%d_", localcount++);
|
||||
Node(tempname);
|
||||
join(tempname, nobj->name);
|
||||
Fprintf(stderr, "Note: Implicit pin %s in instance "
|
||||
Fprintf(stdout, "Note: Implicit pin %s in instance "
|
||||
"%s of %s in cell %s\n",
|
||||
scan->name, sobj->instance.name,
|
||||
modulename, CurrentCell->name);
|
||||
|
||||
@@ -17,6 +17,7 @@ along with this program; see the file copying. If not, write to
|
||||
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* inetcomp.c -- a simple wrapper to the NETCOMP() function */
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "netgen.h"
|
||||
|
||||
@@ -17,6 +17,7 @@ along with this program; see the file copying. If not, write to
|
||||
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* netcomp.c -- a simple wrapper to provide netlist comparison functionality */
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "netgen.h"
|
||||
|
||||
@@ -23,6 +23,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
#include <stdlib.h> /* for getenv */
|
||||
#endif
|
||||
#include "netgen.h"
|
||||
#include "print.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
@@ -20,9 +20,11 @@ along with this program; see the file copying. If not, write to
|
||||
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* ntk2xnf.c -- a simple wrapper to translate .ntk to Xilinx XNF format */
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "netgen.h"
|
||||
#include "xilinx.h"
|
||||
|
||||
#ifdef HAVE_X11
|
||||
/* the following two X procedures are to permit linking
|
||||
|
||||
Vendored
+699
-418
File diff suppressed because it is too large
Load Diff
Vendored
+1487
-1430
File diff suppressed because it is too large
Load Diff
Vendored
+21
-55
@@ -668,7 +668,6 @@ XMKMF
|
||||
HAVE_PYTHON3
|
||||
EGREP
|
||||
GREP
|
||||
M4
|
||||
RANLIB
|
||||
INSTALL_DATA
|
||||
INSTALL_SCRIPT
|
||||
@@ -712,6 +711,7 @@ infodir
|
||||
docdir
|
||||
oldincludedir
|
||||
includedir
|
||||
runstatedir
|
||||
localstatedir
|
||||
sharedstatedir
|
||||
sysconfdir
|
||||
@@ -794,6 +794,7 @@ datadir='${datarootdir}'
|
||||
sysconfdir='${prefix}/etc'
|
||||
sharedstatedir='${prefix}/com'
|
||||
localstatedir='${prefix}/var'
|
||||
runstatedir='${localstatedir}/run'
|
||||
includedir='${prefix}/include'
|
||||
oldincludedir='/usr/include'
|
||||
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
|
||||
@@ -1046,6 +1047,15 @@ do
|
||||
| -silent | --silent | --silen | --sile | --sil)
|
||||
silent=yes ;;
|
||||
|
||||
-runstatedir | --runstatedir | --runstatedi | --runstated \
|
||||
| --runstate | --runstat | --runsta | --runst | --runs \
|
||||
| --run | --ru | --r)
|
||||
ac_prev=runstatedir ;;
|
||||
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
|
||||
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
|
||||
| --run=* | --ru=* | --r=*)
|
||||
runstatedir=$ac_optarg ;;
|
||||
|
||||
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
|
||||
ac_prev=sbindir ;;
|
||||
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
||||
@@ -1183,7 +1193,7 @@ fi
|
||||
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
||||
datadir sysconfdir sharedstatedir localstatedir includedir \
|
||||
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
||||
libdir localedir mandir
|
||||
libdir localedir mandir runstatedir
|
||||
do
|
||||
eval ac_val=\$$ac_var
|
||||
# Remove trailing slashes.
|
||||
@@ -1336,6 +1346,7 @@ Fine tuning of the installation directories:
|
||||
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
||||
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
||||
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
||||
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
|
||||
--libdir=DIR object code libraries [EPREFIX/lib]
|
||||
--includedir=DIR C header files [PREFIX/include]
|
||||
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
||||
@@ -3690,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"
|
||||
@@ -6065,7 +6026,6 @@ fi
|
||||
|
||||
|
||||
if test $usingTcl ; then
|
||||
usingX11=1
|
||||
cadinstall="$cadinstall tcltk"
|
||||
modules="$modules tcltk"
|
||||
programs="$programs tcltk"
|
||||
@@ -6075,7 +6035,11 @@ if test $usingTcl ; then
|
||||
|
||||
extra_libs="$extra_libs \${NETGENDIR}/tcltk/libtcltk.o"
|
||||
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
|
||||
if test $usingX11 ; then
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS"
|
||||
else
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
|
||||
fi
|
||||
else
|
||||
programs="$programs netgen"
|
||||
unused="$unused tcltk"
|
||||
@@ -6160,6 +6124,7 @@ if test $usingTcl ; then
|
||||
# Tk libraries and header files
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
if test $usingX11 ; then
|
||||
if test "${TK_INC_DIR}" != "/usr/include" ; then
|
||||
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
|
||||
fi
|
||||
@@ -6176,6 +6141,7 @@ if test $usingTcl ; then
|
||||
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -6285,7 +6251,7 @@ if test $usingTcl ; then
|
||||
|
||||
*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
|
||||
@@ -807,7 +801,6 @@ dnl "make" instead of requiring "make tcl"
|
||||
dnl ----------------------------------------------------------------
|
||||
|
||||
if test $usingTcl ; then
|
||||
usingX11=1
|
||||
cadinstall="$cadinstall tcltk"
|
||||
modules="$modules tcltk"
|
||||
programs="$programs tcltk"
|
||||
@@ -816,7 +809,11 @@ if test $usingTcl ; then
|
||||
AC_DEFINE(TCL_NETGEN)
|
||||
extra_libs="$extra_libs \${NETGENDIR}/tcltk/libtcltk.o"
|
||||
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
|
||||
if test $usingX11 ; then
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS"
|
||||
else
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
|
||||
fi
|
||||
else
|
||||
programs="$programs netgen"
|
||||
unused="$unused tcltk"
|
||||
@@ -903,6 +900,7 @@ if test $usingTcl ; then
|
||||
# Tk libraries and header files
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
if test $usingX11 ; then
|
||||
if test "${TK_INC_DIR}" != "/usr/include" ; then
|
||||
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
|
||||
fi
|
||||
@@ -919,6 +917,7 @@ if test $usingTcl ; then
|
||||
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
@@ -1028,7 +1027,7 @@ if test $usingTcl ; then
|
||||
|
||||
*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"
|
||||
;;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+31
-4
@@ -479,8 +479,12 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
|
||||
netgen::compare assign "$fnum1 $cell1" "$fnum2 $cell2"
|
||||
|
||||
if {[file exists $setupfile]} {
|
||||
puts stdout "Reading setup file $setupfile"
|
||||
if {$setupfile == ""} {
|
||||
puts stdout "\nNo setup file specified. Using trivial default setup.\n"
|
||||
netgen::permute default ;# transistors and resistors
|
||||
netgen::property default
|
||||
} elseif {[file exists $setupfile]} {
|
||||
puts stdout "\nReading setup file $setupfile\n"
|
||||
# Instead of sourcing the setup file, run each line so we can
|
||||
# catch individual errors and not let them halt the LVS process
|
||||
set perrors 0
|
||||
@@ -508,8 +512,10 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
puts stdout "Warning: There were errors reading the setup file"
|
||||
}
|
||||
} elseif {[string first nosetup $setupfile] < 0} {
|
||||
netgen::permute default ;# transistors and resistors
|
||||
netgen::property default
|
||||
puts stderr "\nError: Setup file $setupfile does not exist.\n"
|
||||
return
|
||||
} else {
|
||||
puts stderr "\nNo setup file specified. Continuing without a setup.\n"
|
||||
}
|
||||
|
||||
if {[string first nolog $logfile] < 0} {
|
||||
@@ -588,6 +594,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
lappend properr [lindex $endval 0]
|
||||
} elseif {$uresult == -2} { ;# unmatched pins
|
||||
set doCheckFlatten 1
|
||||
} elseif {$uresult == -4} { ;# unmatched pins and properties
|
||||
lappend properr [lindex $endval 0]
|
||||
set doCheckFlatten 1
|
||||
}
|
||||
} else {
|
||||
# not equivalent
|
||||
@@ -649,6 +658,11 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
}
|
||||
} elseif {[netgen::print queue] == {} && $result == 0} {
|
||||
set pinMismatch 1
|
||||
} else {
|
||||
# This assumes that proxy pins are added correctly. Previously,
|
||||
# that was not trusted, and so an initial pin mismatch would
|
||||
# always force subcells to be flattened.
|
||||
set doFlatten 0
|
||||
}
|
||||
}
|
||||
if {$doFlatten} {
|
||||
@@ -735,6 +749,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.
|
||||
|
||||
+159
-33
@@ -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) {
|
||||
@@ -2228,7 +2230,13 @@ _netcmp_compare(ClientData clientData,
|
||||
Fprintf(stdout, "\nCircuit 1 cell %s is a black box; will not flatten "
|
||||
"Circuit 2\n", name1);
|
||||
}
|
||||
else if (hascontents1 || hascontents2) {
|
||||
else if (!hascontents1 && !hascontents2 && (tp1->flags & CELL_PLACEHOLDER)
|
||||
&& (tp2->flags & CELL_PLACEHOLDER)) {
|
||||
/* Two empty subcircuits, don't flatten anything */
|
||||
Fprintf(stdout, "\nCircuit 1 cell %s and Circuit 2 cell %s are black"
|
||||
" boxes.\n", name1, name2);
|
||||
}
|
||||
else {
|
||||
FlattenUnmatched(tp1, name1, 1, 0);
|
||||
FlattenUnmatched(tp2, name2, 1, 0);
|
||||
DescribeContents(name1, fnum1, name2, fnum2);
|
||||
@@ -2241,10 +2249,6 @@ _netcmp_compare(ClientData clientData,
|
||||
DescribeContents(name1, fnum1, name2, fnum2);
|
||||
}
|
||||
}
|
||||
else { /* Two empty subcircuits */
|
||||
Fprintf(stdout, "\nCircuit 1 cell %s and Circuit 2 cell %s are black"
|
||||
" boxes.\n", name1, name2);
|
||||
}
|
||||
CreateTwoLists(name1, fnum1, name2, fnum2, dolist);
|
||||
|
||||
// Return the names of the two cells being compared, if doing "compare
|
||||
@@ -2508,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
|
||||
@@ -2518,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");
|
||||
@@ -2554,6 +2572,7 @@ _netcmp_run(ClientData clientData,
|
||||
/* 0: not verified */
|
||||
/* -1: no elements or nodes */
|
||||
/* -3: verified with property error */
|
||||
/* -4: verified with property and port errors */
|
||||
/* equiv option */
|
||||
/* -2: pin mismatch */
|
||||
/* */
|
||||
@@ -2647,14 +2666,20 @@ _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)
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
|
||||
else if (index == UNIQUE_IDX)
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(-2));
|
||||
else if (index == UNIQUE_IDX) {
|
||||
if (PropertyErrorDetected == 0)
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(-2));
|
||||
else
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(-4));
|
||||
}
|
||||
else if (index > 0)
|
||||
Fprintf(stdout, "Circuits match uniquely with port errors.\n");
|
||||
}
|
||||
@@ -2900,7 +2925,7 @@ _netcmp_equate(ClientData clientData,
|
||||
struct ElementClass *saveEclass = NULL;
|
||||
struct NodeClass *saveNclass = NULL;
|
||||
int file1, file2;
|
||||
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
|
||||
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0, dounique = 0;
|
||||
Tcl_Obj *tobj1, *tobj2, *tobj3;
|
||||
|
||||
while (objc > 1) {
|
||||
@@ -2916,6 +2941,11 @@ _netcmp_equate(ClientData clientData,
|
||||
objv++;
|
||||
objc--;
|
||||
}
|
||||
else if (!strcmp(optstart, "unique")) {
|
||||
dounique = 1;
|
||||
objv++;
|
||||
objc--;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -3340,19 +3370,9 @@ _netcmp_equate(ClientData clientData,
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that all pins are assigned by name, reorder */
|
||||
/* the pin lists of the 2nd cell to match the */
|
||||
/* order of the 1st. */
|
||||
|
||||
/* Reorder the pin lists of instances of the 2nd */
|
||||
/* cell to match the order of the 1st. */
|
||||
|
||||
// pindata.cell2 = tp2;
|
||||
// RecurseCellHashTable2(pinorder, (void *)(&pindata));
|
||||
}
|
||||
|
||||
if (EquivalenceClasses(tp1->name, file1, tp2->name, file2)) {
|
||||
if (EquivalenceClasses(tp1->name, file1, tp2->name, file2, dounique)) {
|
||||
Fprintf(stdout, "Device classes %s and %s are equivalent.\n",
|
||||
tp1->name, tp2->name);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
|
||||
@@ -3375,6 +3395,8 @@ _netcmp_equate(ClientData clientData,
|
||||
/* add --- add new property */
|
||||
/* remove --- delete existing property */
|
||||
/* tolerance --- set property tolerance */
|
||||
/* associate --- associate property with a pin */
|
||||
/* topology --- set exact/relaxed matching */
|
||||
/* merge --- (deprecated) */
|
||||
/* or */
|
||||
/* netgen::property default */
|
||||
@@ -3411,13 +3433,21 @@ _netcmp_property(ClientData clientData,
|
||||
double dval;
|
||||
int ival, argstart;
|
||||
|
||||
char *topoptions[] = {
|
||||
"default", "series", "serial", "parallel", "topology", NULL
|
||||
};
|
||||
enum TopOptionIdx {
|
||||
TOP_DEFAULT_IDX, TOP_SERIES_IDX, TOP_SERIAL_IDX, TOP_PARALLEL_IDX,
|
||||
TOP_TOPOLOGY_IDX
|
||||
};
|
||||
|
||||
char *options[] = {
|
||||
"add", "create", "remove", "delete", "tolerance", "merge", "serial",
|
||||
"series", "parallel", NULL
|
||||
"series", "parallel", "associate", "derive", NULL
|
||||
};
|
||||
enum OptionIdx {
|
||||
ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX,
|
||||
SERIAL_IDX, SERIES_IDX, PARALLEL_IDX
|
||||
SERIAL_IDX, SERIES_IDX, PARALLEL_IDX, ASSOCIATE_IDX, DERIVE_IDX
|
||||
};
|
||||
int result, index, idx2;
|
||||
|
||||
@@ -3452,6 +3482,14 @@ _netcmp_property(ClientData clientData,
|
||||
COMB_NONE_IDX, COMB_PAR_IDX, COMB_ADD_IDX, COMB_CRITICAL_IDX
|
||||
};
|
||||
|
||||
char *deriveoptions[] = {
|
||||
"area", "perimeter", NULL
|
||||
};
|
||||
|
||||
enum DeriveOptionIdx {
|
||||
AREA_IDX, PERIMETER_IDX
|
||||
};
|
||||
|
||||
char *yesno[] = {
|
||||
"on", "yes", "true", "enable", "allow",
|
||||
"off", "no", "false", "disable", "prohibit", NULL
|
||||
@@ -3462,8 +3500,17 @@ _netcmp_property(ClientData clientData,
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
char *topo[] = {
|
||||
"strict", "relaxed", NULL
|
||||
};
|
||||
|
||||
/* Don't need to check return value */
|
||||
index = -1;
|
||||
Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)topoptions,
|
||||
"option", 0, &index);
|
||||
|
||||
/* Check for special command "property default" */
|
||||
if ((objc == 2) && (!strcmp(Tcl_GetString(objv[1]), "default"))) {
|
||||
if ((objc == 2) && (index == TOP_DEFAULT_IDX)) {
|
||||
|
||||
/* For each FET device, do "merge {w add_critical}" and */
|
||||
/* "remove as ad ps pd". This allows parallel devices */
|
||||
@@ -3510,7 +3557,7 @@ _netcmp_property(ClientData clientData,
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
else if ((objc == 3) && (!strcmp(Tcl_GetString(objv[1]), "parallel"))) {
|
||||
else if ((objc == 3) && (index == TOP_PARALLEL_IDX)) {
|
||||
if (!strcmp(Tcl_GetString(objv[2]), "none")) {
|
||||
GlobalParallelNone = TRUE;
|
||||
SetParallelCombine(FALSE);
|
||||
@@ -3532,8 +3579,7 @@ _netcmp_property(ClientData clientData,
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
else if ((objc == 3) && ((!strcmp(Tcl_GetString(objv[1]), "series")) ||
|
||||
(!strcmp(Tcl_GetString(objv[1]), "serial")))) {
|
||||
else if ((objc == 3) && ((index == TOP_SERIES_IDX) || (index == TOP_SERIAL_IDX))) {
|
||||
if (!strcmp(Tcl_GetString(objv[2]), "none")) {
|
||||
SetSeriesCombine(FALSE);
|
||||
}
|
||||
@@ -3547,6 +3593,36 @@ _netcmp_property(ClientData clientData,
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
else if ((objc > 1) && (index == TOP_TOPOLOGY_IDX)) {
|
||||
if (objc == 2) {
|
||||
if (ExactTopology)
|
||||
Tcl_SetResult(interp, "Strict topology property matching.",
|
||||
NULL);
|
||||
else
|
||||
Tcl_SetResult(interp, "Relaxed topology property matching.",
|
||||
NULL);
|
||||
}
|
||||
else if (objc == 3) {
|
||||
if (Tcl_GetIndexFromObj(interp, objv[2],
|
||||
(CONST84 char **)topo,
|
||||
"topology", 0, &idx2) == TCL_OK) {
|
||||
if (idx2 == 0)
|
||||
ExactTopology = TRUE;
|
||||
else if (idx2 == 1)
|
||||
ExactTopology = FALSE;
|
||||
else {
|
||||
Tcl_SetResult(interp, "Topology matching type must be "
|
||||
"'strict' or 'relaxed'.", NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "strict|relaxed");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
result = CommonParseCell(interp, objv[1], &tp, &fnum);
|
||||
if (result != TCL_OK) return result;
|
||||
@@ -3840,6 +3916,29 @@ _netcmp_property(ClientData clientData,
|
||||
}
|
||||
break;
|
||||
|
||||
case ASSOCIATE_IDX:
|
||||
if (objc == 3) {
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "{property_key pin_name} ...");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for (i = 3; i < objc; i++) {
|
||||
// Each value must be a duplet
|
||||
result = Tcl_ListObjLength(interp, objv[i], &llen);
|
||||
if ((result != TCL_OK) || (llen != 2)) {
|
||||
Tcl_SetResult(interp, "Not a {key pin} pair list.",
|
||||
NULL);
|
||||
}
|
||||
else {
|
||||
result = Tcl_ListObjIndex(interp, objv[i], 0, &tobj1);
|
||||
if (result != TCL_OK) return result;
|
||||
result = Tcl_ListObjIndex(interp, objv[i], 1, &tobj2);
|
||||
if (result != TCL_OK) return result;
|
||||
PropertyAssociatePin(tp->name, fnum, Tcl_GetString(tobj1),
|
||||
Tcl_GetString(tobj2));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TOLERANCE_IDX:
|
||||
if (objc == 3) {
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "{property_key tolerance} ...");
|
||||
@@ -3939,6 +4038,33 @@ _netcmp_property(ClientData clientData,
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DERIVE_IDX:
|
||||
/* Create a derived property. For now, this just creates
|
||||
* "area" or "perimeter" properties. There may not (?)
|
||||
* be enough cause to make other ones. Unlike other
|
||||
* "property" options, this option causes the whole circuit
|
||||
* database to be searched for the device in question, and
|
||||
* the new property is added.
|
||||
*/
|
||||
if (objc < 6) {
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "{area|perimeter width length}");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
result = Tcl_GetIndexFromObj(interp, objv[3],
|
||||
(CONST84 char **)deriveoptions,
|
||||
"area|perimeter", 0, &idx2);
|
||||
if (result != TCL_OK) return result;
|
||||
switch (idx2) {
|
||||
case AREA_IDX:
|
||||
DeriveAreaProperty(tp, fnum, Tcl_GetString(objv[4]),
|
||||
Tcl_GetString(objv[5]));
|
||||
break;
|
||||
case PERIMETER_IDX:
|
||||
DerivePerimeterProperty(tp, fnum, Tcl_GetString(objv[4]),
|
||||
Tcl_GetString(objv[5]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TCL_OK;
|
||||
|
||||
+123
-80
@@ -36,7 +36,7 @@ exec ${NETGEN_WISH:=wish} "$0" ${1+"$@"}
|
||||
#
|
||||
# set ::tkcon::PRIV(proxy) {wwwproxy:8080 1}
|
||||
#
|
||||
# Or you can set the above variable from within tkcon by calling
|
||||
# Or you can set the above variable from within tkcon by calling
|
||||
#
|
||||
# tkcon master set ::tkcon:PRIV(proxy) wwwproxy:8080
|
||||
#
|
||||
@@ -44,6 +44,8 @@ exec ${NETGEN_WISH:=wish} "$0" ${1+"$@"}
|
||||
if {$tcl_version < 8.0} {
|
||||
return -code error "tkcon requires at least Tcl/Tk8"
|
||||
} else {
|
||||
# Prevent breaking on version 8.5.2
|
||||
# package require -exact Tk $tcl_version
|
||||
package require Tk $tcl_version
|
||||
}
|
||||
|
||||
@@ -59,18 +61,6 @@ foreach pkg [info loaded {}] {
|
||||
}
|
||||
catch {unset pkg file name version}
|
||||
|
||||
# Tk 8.4 makes previously exposed stuff private.
|
||||
# FIX: Update tkcon to not rely on the private Tk code.
|
||||
#
|
||||
if {![llength [info globals tkPriv]]} {
|
||||
::tk::unsupported::ExposePrivateVariable tkPriv
|
||||
}
|
||||
foreach cmd {SetCursor UpDownLine Transpose ScrollPages} {
|
||||
if {![llength [info commands tkText$cmd]]} {
|
||||
::tk::unsupported::ExposePrivateCommand tkText$cmd
|
||||
}
|
||||
}
|
||||
|
||||
# Initialize the ::tkcon namespace
|
||||
#
|
||||
namespace eval ::tkcon {
|
||||
@@ -196,7 +186,7 @@ proc ::tkcon::Init {} {
|
||||
tkcon_puts tkcon_gets observe observe_var unalias which what
|
||||
}
|
||||
version 2.3
|
||||
RCS {RCS: @(#) $Id: tkcon.tcl,v 1.2 2008/05/23 00:20:17 tim Exp $}
|
||||
RCS {RCS: @(#) $Id: tkcon.tcl,v 1.2 2008/04/18 16:28:13 tim Exp $}
|
||||
HEADURL {http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tkcon/tkcon/tkcon.tcl?rev=HEAD}
|
||||
docs "http://tkcon.sourceforge.net/"
|
||||
email {jeff@hobbs.org}
|
||||
@@ -654,7 +644,7 @@ proc ::tkcon::GarbageCollect {} {
|
||||
## ::tkcon::EvalCmd) in turn. Any uncompleted command will not be eval'ed.
|
||||
# ARGS: w - console text widget
|
||||
# Calls: ::tkcon::CmdGet, ::tkcon::CmdSep, ::tkcon::EvalCmd
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Eval {w} {
|
||||
set incomplete [CmdSep [CmdGet $w] cmds last]
|
||||
$w mark set insert end-1c
|
||||
@@ -674,7 +664,7 @@ proc ::tkcon::Eval {w} {
|
||||
# Calls: ::tkcon::Prompt
|
||||
# Outputs: result of command to stdout (or stderr if error occured)
|
||||
# Returns: next event number
|
||||
##
|
||||
##
|
||||
proc ::tkcon::EvalCmd {w cmd} {
|
||||
variable OPT
|
||||
variable PRIV
|
||||
@@ -745,7 +735,7 @@ proc ::tkcon::EvalCmd {w cmd} {
|
||||
$w tag bind $tag <Leave> \
|
||||
[list $w tag configure $tag -underline 0]
|
||||
$w tag bind $tag <ButtonRelease-1> \
|
||||
"if {!\[info exists tkPriv(mouseMoved)\] || !\$tkPriv(mouseMoved)} \
|
||||
"if {!\[info exists ::tk::Priv(mouseMoved)\] || !\$::tk::Priv(mouseMoved)} \
|
||||
{[list edit -attach [Attach] -type error -- $PRIV(errorInfo)]}"
|
||||
} else {
|
||||
$w insert output $res\n stderr
|
||||
@@ -905,7 +895,7 @@ proc ::tkcon::EvalSocketClosed {} {
|
||||
## ::tkcon::EvalNamespace - evaluates the args in a particular namespace
|
||||
## This is an override for ::tkcon::EvalAttached for when the user wants
|
||||
## to attach to a particular namespace of the attached interp
|
||||
# ARGS: attached
|
||||
# ARGS: attached
|
||||
# namespace the namespace to evaluate in
|
||||
# args the args to evaluate
|
||||
# RETURNS: the result of the command
|
||||
@@ -933,7 +923,7 @@ proc ::tkcon::Namespaces {{ns ::} {l {}}} {
|
||||
## ::tkcon::CmdGet - gets the current command from the console widget
|
||||
# ARGS: w - console text widget
|
||||
# Returns: text which compromises current command line
|
||||
##
|
||||
##
|
||||
proc ::tkcon::CmdGet w {
|
||||
if {![llength [$w tag nextrange prompt limit end]]} {
|
||||
$w tag add stdin limit end-1c
|
||||
@@ -947,7 +937,7 @@ proc ::tkcon::CmdGet w {
|
||||
# last - varname of any remainder (like an incomplete final command).
|
||||
# If there is only one command, it's placed in this var.
|
||||
# Returns: constituent command info in varnames specified by list & rmd.
|
||||
##
|
||||
##
|
||||
proc ::tkcon::CmdSep {cmd list last} {
|
||||
upvar 1 $list cmds $last inc
|
||||
set inc {}
|
||||
@@ -974,7 +964,7 @@ proc ::tkcon::CmdSep {cmd list last} {
|
||||
## ::tkcon::CmdSplit - splits multiple commands into a list
|
||||
# ARGS: cmd - (possible) multiple command to separate
|
||||
# Returns: constituent commands in a list
|
||||
##
|
||||
##
|
||||
proc ::tkcon::CmdSplit {cmd} {
|
||||
set inc {}
|
||||
set cmds {}
|
||||
@@ -998,7 +988,7 @@ proc ::tkcon::CmdSplit {cmd} {
|
||||
## Called by ::tkcon::EvalCmd
|
||||
# ARGS: w - text widget
|
||||
# Outputs: tag name guaranteed unique in the widget
|
||||
##
|
||||
##
|
||||
proc ::tkcon::UniqueTag {w} {
|
||||
set tags [$w tag names]
|
||||
set idx 0
|
||||
@@ -1011,7 +1001,7 @@ proc ::tkcon::UniqueTag {w} {
|
||||
# ARGS: w - console text widget
|
||||
# size - # of lines to constrain to
|
||||
# Outputs: may delete data in console widget
|
||||
##
|
||||
##
|
||||
proc ::tkcon::ConstrainBuffer {w size} {
|
||||
if {[$w index end] > $size} {
|
||||
$w delete 1.0 [expr {int([$w index end])-$size}].0
|
||||
@@ -1021,7 +1011,7 @@ proc ::tkcon::ConstrainBuffer {w size} {
|
||||
## ::tkcon::Prompt - displays the prompt in the console widget
|
||||
# ARGS: w - console text widget
|
||||
# Outputs: prompt (specified in ::tkcon::OPT(prompt1)) to console
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Prompt {{pre {}} {post {}} {prompt {}}} {
|
||||
variable OPT
|
||||
variable PRIV
|
||||
@@ -1053,7 +1043,7 @@ proc ::tkcon::Prompt {{pre {}} {post {}} {prompt {}}} {
|
||||
}
|
||||
|
||||
## ::tkcon::About - gives about info for tkcon
|
||||
##
|
||||
##
|
||||
proc ::tkcon::About {} {
|
||||
variable OPT
|
||||
variable PRIV
|
||||
@@ -1088,7 +1078,7 @@ proc ::tkcon::About {} {
|
||||
|
||||
## ::tkcon::InitMenus - inits the menubar and popup for the console
|
||||
# ARGS: w - console text widget
|
||||
##
|
||||
##
|
||||
proc ::tkcon::InitMenus {w title} {
|
||||
variable OPT
|
||||
variable PRIV
|
||||
@@ -1521,7 +1511,7 @@ proc ::tkcon::NamespaceMenu m {
|
||||
}
|
||||
}
|
||||
|
||||
## Namepaces List
|
||||
## Namepaces List
|
||||
##
|
||||
proc ::tkcon::NamespacesList {names} {
|
||||
variable PRIV
|
||||
@@ -1894,7 +1884,7 @@ proc ::tkcon::NewSocket {} {
|
||||
## The file is actually sourced in the currently attached's interp
|
||||
# ARGS: fn - (optional) filename to source in
|
||||
# Returns: selected filename ({} if nothing was selected)
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Load { {fn ""} } {
|
||||
set types {
|
||||
{{Tcl Files} {.tcl .tk}}
|
||||
@@ -1913,7 +1903,7 @@ proc ::tkcon::Load { {fn ""} } {
|
||||
## This does not eval in a slave because it's not necessary
|
||||
# ARGS: w - console text widget
|
||||
# fn - (optional) filename to save to
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Save { {fn ""} {type ""} {opt ""} {mode w} } {
|
||||
variable PRIV
|
||||
|
||||
@@ -1983,7 +1973,7 @@ proc ::tkcon::MainInit {} {
|
||||
## Creates a slave interpreter and sources in this script.
|
||||
## All other interpreters also get a command to eval function in the
|
||||
## new interpreter.
|
||||
##
|
||||
##
|
||||
proc ::tkcon::New {} {
|
||||
variable PRIV
|
||||
global argv0 argc argv
|
||||
@@ -2020,7 +2010,7 @@ proc ::tkcon::MainInit {} {
|
||||
## ::tkcon::Exit - full exit OR destroy slave console
|
||||
## This proc should only be called in the main interpreter from a slave.
|
||||
## The master determines whether we do a full exit or just kill the slave.
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Exit {slave args} {
|
||||
variable PRIV
|
||||
variable OPT
|
||||
@@ -2043,7 +2033,7 @@ proc ::tkcon::MainInit {} {
|
||||
## This proc should only be called by the main interpreter. If it is
|
||||
## called from there, it will ask before exiting tkcon. All others
|
||||
## (slaves) will just have their slave interpreter deleted, closing them.
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Destroy {{slave {}}} {
|
||||
variable PRIV
|
||||
|
||||
@@ -2474,7 +2464,7 @@ proc ::tkcon::ErrorHighlight w {
|
||||
$w tag configure $tag -foreground $COLOR(stdout)
|
||||
$w tag bind $tag <Enter> [list $w tag configure $tag -underline 1]
|
||||
$w tag bind $tag <Leave> [list $w tag configure $tag -underline 0]
|
||||
$w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
|
||||
$w tag bind $tag <ButtonRelease-1> "if {!\$::tk::Priv(mouseMoved)} \
|
||||
{[list edit -attach $app -type proc -find $what -- $cmd]}"
|
||||
}
|
||||
set info [string range $info $c1 end]
|
||||
@@ -2503,7 +2493,7 @@ proc ::tkcon::ErrorHighlight w {
|
||||
$w tag configure $tag -foreground $COLOR(proc)
|
||||
$w tag bind $tag <Enter> [list $w tag configure $tag -underline 1]
|
||||
$w tag bind $tag <Leave> [list $w tag configure $tag -underline 0]
|
||||
$w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
|
||||
$w tag bind $tag <ButtonRelease-1> "if {!\$::tk::Priv(mouseMoved)} \
|
||||
{[list edit -attach $app -type proc -- $cmd]}"
|
||||
}
|
||||
}
|
||||
@@ -2513,7 +2503,7 @@ proc ::tkcon::ErrorHighlight w {
|
||||
## This always exists in the main interpreter, and is aliased into
|
||||
## other connected interpreters
|
||||
# ARGS: totally variable, see internal comments
|
||||
##
|
||||
##
|
||||
proc tkcon {cmd args} {
|
||||
global errorInfo
|
||||
|
||||
@@ -2552,8 +2542,8 @@ proc tkcon {cmd args} {
|
||||
## 'congets' a replacement for [gets stdin]
|
||||
# Use the 'gets' alias of 'tkcon_gets' command instead of
|
||||
# calling the *get* methods directly for best compatability
|
||||
if {[llength $args]} {
|
||||
return -code error "wrong # args: must be \"tkcon congets\""
|
||||
if {[llength $args] > 1} {
|
||||
return -code error "wrong # args: must be \"tkcon congets [pfix]\""
|
||||
}
|
||||
tkcon show
|
||||
set old [bind TkConsole <<TkCon_Eval>>]
|
||||
@@ -2561,7 +2551,12 @@ proc tkcon {cmd args} {
|
||||
set w $::tkcon::PRIV(console)
|
||||
# Make sure to move the limit to get the right data
|
||||
$w mark set insert end
|
||||
$w mark set limit insert
|
||||
if {[llength $args]} {
|
||||
$w mark set limit insert
|
||||
$w insert end $args
|
||||
} else {
|
||||
$w mark set limit insert
|
||||
}
|
||||
$w see end
|
||||
vwait ::tkcon::PRIV(wait)
|
||||
set line [::tkcon::CmdGet $w]
|
||||
@@ -2790,21 +2785,27 @@ proc tkcon {cmd args} {
|
||||
## This allows me to capture all stdout/stderr to the console window
|
||||
## This will be renamed to 'puts' at the appropriate time during init
|
||||
##
|
||||
# ARGS: same as usual
|
||||
# ARGS: same as usual
|
||||
# Outputs: the string with a color-coded text tag
|
||||
##
|
||||
##
|
||||
proc tkcon_puts args {
|
||||
set len [llength $args]
|
||||
foreach {arg1 arg2 arg3} $args { break }
|
||||
|
||||
if {$len == 1} {
|
||||
tkcon console insert output "$arg1\n" stdout
|
||||
set sarg $arg1
|
||||
set nl 1
|
||||
set farg stdout
|
||||
} elseif {$len == 2} {
|
||||
if {![string compare $arg1 -nonewline]} {
|
||||
tkcon console insert output $arg2 stdout
|
||||
set sarg $arg2
|
||||
set farg stdout
|
||||
set nl 0
|
||||
} elseif {![string compare $arg1 stdout] \
|
||||
|| ![string compare $arg1 stderr]} {
|
||||
tkcon console insert output "$arg2\n" $arg1
|
||||
set sarg $arg2
|
||||
set farg $arg1
|
||||
set nl 1
|
||||
} else {
|
||||
set len 0
|
||||
}
|
||||
@@ -2812,11 +2813,15 @@ proc tkcon_puts args {
|
||||
if {![string compare $arg1 -nonewline] \
|
||||
&& (![string compare $arg2 stdout] \
|
||||
|| ![string compare $arg2 stderr])} {
|
||||
tkcon console insert output $arg3 $arg2
|
||||
set sarg $arg3
|
||||
set farg $arg2
|
||||
set nl 0
|
||||
} elseif {(![string compare $arg1 stdout] \
|
||||
|| ![string compare $arg1 stderr]) \
|
||||
&& ![string compare $arg3 nonewline]} {
|
||||
tkcon console insert output $arg2 $arg1
|
||||
set sarg $arg2
|
||||
set farg $arg1
|
||||
set nl 0
|
||||
} else {
|
||||
set len 0
|
||||
}
|
||||
@@ -2826,7 +2831,42 @@ proc tkcon_puts args {
|
||||
|
||||
## $len == 0 means it wasn't handled by tkcon above.
|
||||
##
|
||||
if {$len == 0} {
|
||||
|
||||
if {$len != 0} {
|
||||
|
||||
## "poor man's" \r substitution---erase everything on the output
|
||||
## line and print from character after the \r
|
||||
|
||||
set rpt [string last \r $sarg]
|
||||
if {$rpt >= 0} {
|
||||
tkcon console delete "insert linestart" "insert lineend"
|
||||
set sarg [string range $sarg [expr {$rpt + 1}] end]
|
||||
}
|
||||
|
||||
set bpt [string first \b $sarg]
|
||||
if {$bpt >= 0} {
|
||||
set narg [string range $sarg [expr {$bpt + 1}] end]
|
||||
set sarg [string range $sarg 0 [expr {$bpt - 1}]]
|
||||
set nl 0
|
||||
}
|
||||
|
||||
|
||||
if {$nl == 0} {
|
||||
tkcon console insert output $sarg $farg
|
||||
} else {
|
||||
tkcon console insert output "$sarg\n" $farg
|
||||
}
|
||||
|
||||
if {$bpt >= 0} {
|
||||
tkcon console delete "insert -1 char" insert
|
||||
if {$nl == 0} {
|
||||
tkcon_puts $farg $narg nonewline
|
||||
} else {
|
||||
tkcon_puts $farg $narg
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
global errorCode errorInfo
|
||||
if {[catch "tkcon_tcl_puts $args" msg]} {
|
||||
regsub tkcon_tcl_puts $msg puts msg
|
||||
@@ -2849,7 +2889,7 @@ proc tkcon_puts args {
|
||||
## This allows me to capture all stdin input without needing to stdin
|
||||
## This will be renamed to 'gets' at the appropriate time during init
|
||||
##
|
||||
# ARGS: same as gets
|
||||
# ARGS: same as gets
|
||||
# Outputs: same as gets
|
||||
##
|
||||
proc tkcon_gets args {
|
||||
@@ -2873,12 +2913,12 @@ proc tkcon_gets args {
|
||||
}
|
||||
|
||||
## edit - opens a file/proc/var for reading/editing
|
||||
##
|
||||
##
|
||||
# Arguments:
|
||||
# type proc/file/var
|
||||
# what the actual name of the item
|
||||
# Returns: nothing
|
||||
##
|
||||
##
|
||||
proc edit {args} {
|
||||
array set opts {-find {} -type {} -attach {}}
|
||||
while {[string match -* [lindex $args 0]]} {
|
||||
@@ -3030,7 +3070,7 @@ proc echo args { puts [concat $args] }
|
||||
|
||||
## clear - clears the buffer of the console (not the history though)
|
||||
## This is executed in the parent interpreter
|
||||
##
|
||||
##
|
||||
proc clear {{pcnt 100}} {
|
||||
if {![regexp {^[0-9]*$} $pcnt] || $pcnt < 1 || $pcnt > 100} {
|
||||
return -code error \
|
||||
@@ -3048,7 +3088,7 @@ proc clear {{pcnt 100}} {
|
||||
## If called with one arg, returns the alias of that arg (or {} if none)
|
||||
# ARGS: newcmd - (optional) command to bind alias to
|
||||
# args - command and args being aliased
|
||||
##
|
||||
##
|
||||
proc alias {{newcmd {}} args} {
|
||||
if {[string match {} $newcmd]} {
|
||||
set res {}
|
||||
@@ -3065,7 +3105,7 @@ proc alias {{newcmd {}} args} {
|
||||
|
||||
## unalias - unaliases an alias'ed command
|
||||
# ARGS: cmd - command to unbind as an alias
|
||||
##
|
||||
##
|
||||
proc unalias {cmd} {
|
||||
interp alias {} $cmd {}
|
||||
}
|
||||
@@ -3085,7 +3125,7 @@ proc unalias {cmd} {
|
||||
# -- forcibly ends options recognition
|
||||
#
|
||||
# Returns: the values of the requested items in a 'source'able form
|
||||
##
|
||||
##
|
||||
proc dump {type args} {
|
||||
set whine 1
|
||||
set code ok
|
||||
@@ -3637,13 +3677,13 @@ proc observe_var {name el op} {
|
||||
## which - tells you where a command is found
|
||||
# ARGS: cmd - command name
|
||||
# Returns: where command is found (internal / external / unknown)
|
||||
##
|
||||
##
|
||||
proc which cmd {
|
||||
## This tries to auto-load a command if not recognized
|
||||
set types [uplevel 1 [list what $cmd 1]]
|
||||
if {[llength $types]} {
|
||||
set out {}
|
||||
|
||||
|
||||
foreach type $types {
|
||||
switch -- $type {
|
||||
alias { set res "$cmd: aliased to [alias $cmd]" }
|
||||
@@ -3674,7 +3714,7 @@ proc which cmd {
|
||||
## what - tells you what a string is recognized as
|
||||
# ARGS: str - string to id
|
||||
# Returns: id types of command as list
|
||||
##
|
||||
##
|
||||
proc what {str {autoload 0}} {
|
||||
set types {}
|
||||
if {[llength [info commands $str]] || ($autoload && \
|
||||
@@ -3721,7 +3761,7 @@ proc what {str {autoload 0}} {
|
||||
# -long - list in full format "permissions size date filename"
|
||||
# -full - displays / after directories and link paths for links
|
||||
# Returns: a directory listing
|
||||
##
|
||||
##
|
||||
proc dir {args} {
|
||||
array set s {
|
||||
all 0 full 0 long 0
|
||||
@@ -4106,7 +4146,7 @@ proc ::tkcon::Bindings {} {
|
||||
global tcl_platform tk_version
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Elements of tkPriv that are used in this file:
|
||||
# Elements of ::tk::Priv that are used in this file:
|
||||
#
|
||||
# char - Character position on the line; kept in order
|
||||
# to allow moving up or down past short lines while
|
||||
@@ -4131,9 +4171,12 @@ proc ::tkcon::Bindings {} {
|
||||
}
|
||||
|
||||
## Get all Text bindings into TkConsole
|
||||
foreach ev [bind Text] { bind TkConsole $ev [bind Text $ev] }
|
||||
foreach ev [bind Text] { bind TkConsole $ev [bind Text $ev] }
|
||||
## We really didn't want the newline insertion
|
||||
bind TkConsole <Control-Key-o> {}
|
||||
|
||||
## in 8.6b3, the virtual events <<NextLine>> and <<PrevLine>>
|
||||
# mess up our history feature
|
||||
bind TkConsole <<NextLine>> {}
|
||||
bind TkConsole <<PrevLine>> {}
|
||||
|
||||
@@ -4342,9 +4385,9 @@ proc ::tkcon::Bindings {} {
|
||||
|
||||
bind TkConsole <Control-a> {
|
||||
if {[%W compare {limit linestart} == {insert linestart}]} {
|
||||
tkTextSetCursor %W limit
|
||||
::tk::TextSetCursor %W limit
|
||||
} else {
|
||||
tkTextSetCursor %W {insert linestart}
|
||||
::tk::TextSetCursor %W {insert linestart}
|
||||
}
|
||||
}
|
||||
bind TkConsole <Key-Home> [bind TkConsole <Control-a>]
|
||||
@@ -4368,14 +4411,14 @@ proc ::tkcon::Bindings {} {
|
||||
}
|
||||
bind TkConsole <<TkCon_Previous>> {
|
||||
if {[%W compare {insert linestart} != {limit linestart}]} {
|
||||
tkTextSetCursor %W [tkTextUpDownLine %W -1]
|
||||
::tk::TextSetCursor %W [::tk::TextUpDownLine %W -1]
|
||||
} else {
|
||||
::tkcon::Event -1
|
||||
}
|
||||
}
|
||||
bind TkConsole <<TkCon_Next>> {
|
||||
if {[%W compare {insert linestart} != {end-1c linestart}]} {
|
||||
tkTextSetCursor %W [tkTextUpDownLine %W 1]
|
||||
::tk::TextSetCursor %W [::tk::TextUpDownLine %W 1]
|
||||
} else {
|
||||
::tkcon::Event 1
|
||||
}
|
||||
@@ -4390,7 +4433,7 @@ proc ::tkcon::Bindings {} {
|
||||
}
|
||||
bind TkConsole <<TkCon_Transpose>> {
|
||||
## Transpose current and previous chars
|
||||
if {[%W compare insert > "limit+1c"]} { tkTextTranspose %W }
|
||||
if {[%W compare insert > "limit+1c"]} { ::tk::TextTranspose %W }
|
||||
}
|
||||
bind TkConsole <<TkCon_ClearLine>> {
|
||||
## Clear command line (Unix shell staple)
|
||||
@@ -4408,10 +4451,10 @@ proc ::tkcon::Bindings {} {
|
||||
::tkcon::Insert %W $::tkcon::PRIV(tmp)
|
||||
%W see end
|
||||
}
|
||||
catch {bind TkConsole <Key-Page_Up> { tkTextScrollPages %W -1 }}
|
||||
catch {bind TkConsole <Key-Prior> { tkTextScrollPages %W -1 }}
|
||||
catch {bind TkConsole <Key-Page_Down> { tkTextScrollPages %W 1 }}
|
||||
catch {bind TkConsole <Key-Next> { tkTextScrollPages %W 1 }}
|
||||
catch {bind TkConsole <Key-Page_Up> { ::tk::TextScrollPages %W -1 }}
|
||||
catch {bind TkConsole <Key-Prior> { ::tk::TextScrollPages %W -1 }}
|
||||
catch {bind TkConsole <Key-Page_Down> { ::tk::TextScrollPages %W 1 }}
|
||||
catch {bind TkConsole <Key-Next> { ::tk::TextScrollPages %W 1 }}
|
||||
bind TkConsole <$PRIV(meta)-d> {
|
||||
if {[%W compare insert >= limit]} {
|
||||
%W delete insert {insert wordend}
|
||||
@@ -4429,7 +4472,7 @@ proc ::tkcon::Bindings {} {
|
||||
}
|
||||
bind TkConsole <ButtonRelease-2> {
|
||||
if {
|
||||
(!$tkPriv(mouseMoved) || $tk_strictMotif) &&
|
||||
(!$::tk::Priv(mouseMoved) || $tk_strictMotif) &&
|
||||
![catch {::tkcon::GetSelection %W} ::tkcon::PRIV(tmp)]
|
||||
} {
|
||||
if {[%W compare @%x,%y < limit]} {
|
||||
@@ -4600,7 +4643,7 @@ proc ::tkcon::TagProc w {
|
||||
# c1 - first char of pair
|
||||
# c2 - second char of pair
|
||||
# Calls: ::tkcon::Blink
|
||||
##
|
||||
##
|
||||
proc ::tkcon::MatchPair {w c1 c2 {lim 1.0}} {
|
||||
if {[string compare {} [set ix [$w search -back $c1 insert $lim]]]} {
|
||||
while {
|
||||
@@ -4638,7 +4681,7 @@ proc ::tkcon::MatchPair {w c1 c2 {lim 1.0}} {
|
||||
## The quote to match is assumed to be at the text index 'insert'.
|
||||
# ARGS: w - console text widget
|
||||
# Calls: ::tkcon::Blink
|
||||
##
|
||||
##
|
||||
proc ::tkcon::MatchQuote {w {lim 1.0}} {
|
||||
set i insert-1c
|
||||
set j 0
|
||||
@@ -4664,7 +4707,7 @@ proc ::tkcon::MatchQuote {w {lim 1.0}} {
|
||||
# i2 - end index of blink region
|
||||
# dur - duration in usecs to blink for
|
||||
# Outputs: blinks selected characters in $w
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Blink {w args} {
|
||||
eval [list $w tag add blink] $args
|
||||
after $::tkcon::OPT(blinktime) [list $w] tag remove blink $args
|
||||
@@ -4679,7 +4722,7 @@ proc ::tkcon::Blink {w args} {
|
||||
# ARGS: w - text window in which to insert the string
|
||||
# s - string to insert (usually just a single char)
|
||||
# Outputs: $s to text widget
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Insert {w s} {
|
||||
if {[string match {} $s] || [string match disabled [$w cget -state]]} {
|
||||
return
|
||||
@@ -4695,7 +4738,7 @@ proc ::tkcon::Insert {w s} {
|
||||
$w see insert
|
||||
}
|
||||
|
||||
## ::tkcon::Expand -
|
||||
## ::tkcon::Expand -
|
||||
# ARGS: w - text widget in which to expand str
|
||||
# type - type of expansion (path / proc / variable)
|
||||
# Calls: ::tkcon::Expand(Pathname|Procname|Variable)
|
||||
@@ -4704,7 +4747,7 @@ proc ::tkcon::Insert {w s} {
|
||||
# match equaled the string to expand, then all possible matches
|
||||
# are output to stdout. Triggers bell if no matches are found.
|
||||
# Returns: number of matches found
|
||||
##
|
||||
##
|
||||
proc ::tkcon::Expand {w {type ""}} {
|
||||
set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"$\]"
|
||||
set tmp [$w search -backwards -regexp $exp insert-1c limit-1c]
|
||||
@@ -4743,7 +4786,7 @@ proc ::tkcon::Expand {w {type ""}} {
|
||||
# Calls: ::tkcon::ExpandBestMatch
|
||||
# Returns: list containing longest unique match followed by all the
|
||||
# possible further matches
|
||||
##
|
||||
##
|
||||
proc ::tkcon::ExpandPathname str {
|
||||
set pwd [EvalAttached pwd]
|
||||
# Cause a string like {C:/Program\ Files/} to become "C:/Program Files/"
|
||||
@@ -4825,7 +4868,7 @@ proc ::tkcon::ExpandProcname str {
|
||||
# Calls: ::tkcon::ExpandBestMatch
|
||||
# Returns: list containing longest unique match followed by all the
|
||||
# possible further matches
|
||||
##
|
||||
##
|
||||
proc ::tkcon::ExpandVariable str {
|
||||
if {[regexp {([^\(]*)\((.*)} $str junk ary str]} {
|
||||
## Looks like they're trying to expand an array.
|
||||
@@ -4853,7 +4896,7 @@ proc ::tkcon::ExpandVariable str {
|
||||
## or $e is {}. $e is extra for compatibility with proc below.
|
||||
# ARGS: l - list to find best unique match in
|
||||
# Returns: longest unique match in the list
|
||||
##
|
||||
##
|
||||
proc ::tkcon::ExpandBestMatch2 {l {e {}}} {
|
||||
set s [lindex $l 0]
|
||||
if {[llength $l]>1} {
|
||||
@@ -4873,7 +4916,7 @@ proc ::tkcon::ExpandBestMatch2 {l {e {}}} {
|
||||
# ARGS: l - list to find best unique match in
|
||||
# e - currently best known unique match
|
||||
# Returns: longest unique match in the list
|
||||
##
|
||||
##
|
||||
proc ::tkcon::ExpandBestMatch {l {e {}}} {
|
||||
set ec [lindex $l 0]
|
||||
if {[llength $l]>1} {
|
||||
@@ -5237,7 +5280,7 @@ proc ::tkcon::Retrieve {} {
|
||||
## ::tkcon::Resource - re'source's this script into current console
|
||||
## Meant primarily for my development of this program. It follows
|
||||
## links until the ultimate source is found.
|
||||
##
|
||||
##
|
||||
set ::tkcon::PRIV(SCRIPT) [info script]
|
||||
if {!$::tkcon::PRIV(WWW) && [string compare $::tkcon::PRIV(SCRIPT) {}]} {
|
||||
# we use a catch here because some wrap apps choke on 'file type'
|
||||
|
||||
Reference in New Issue
Block a user