mirror of
https://github.com/RTimothyEdwards/netgen.git
synced 2026-08-22 14:07:07 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb7876c7a6 | ||
|
|
a984ac1a4d | ||
|
|
e31caa3500 | ||
|
|
738c1f7b37 | ||
|
|
c3cf6c3765 | ||
|
|
99dcc20c0a | ||
|
|
c1355bee45 | ||
|
|
4bbc496749 | ||
|
|
4fb8b59a2f | ||
|
|
c4f03eabaf | ||
|
|
6a555ad6ed | ||
|
|
626faf22f9 | ||
|
|
1c5457e180 | ||
|
|
7d246c36a6 | ||
|
|
550234dce5 | ||
|
|
92dfa74403 | ||
|
|
a4c0028706 | ||
|
|
d53541d1d3 | ||
|
|
57b2d21221 | ||
|
|
e7ae84bbc8 | ||
|
|
dfabdaa367 | ||
|
|
c5c1306f6a | ||
|
|
43c018f3bc | ||
|
|
0614461c84 | ||
|
|
6e94886b65 | ||
|
|
ca119edc0a | ||
|
|
0556abf3c1 | ||
|
|
84348d2fe1 | ||
|
|
3356dd784e | ||
|
|
813b29cdc4 | ||
|
|
00949970b4 | ||
|
|
ccf4a0a900 | ||
|
|
c83b6def5b | ||
|
|
e34166fdb9 | ||
|
|
8fc2c097d2 | ||
|
|
a13491e358 | ||
|
|
2ac2a3b6f8 | ||
|
|
1d05b8b676 | ||
|
|
759d63cea9 | ||
|
|
476da015f0 | ||
|
|
4aa8a72769 | ||
|
|
e97d6f1aeb | ||
|
|
47e7367c26 | ||
|
|
362fb7120b | ||
|
|
fb4759a8cd | ||
|
|
92e8fed8be | ||
|
|
45d4004d43 | ||
|
|
01b2484be3 | ||
|
|
fe47d84ace | ||
|
|
0b014e6efd | ||
|
|
0a2adeb593 | ||
|
|
330b34139c | ||
|
|
62d0352149 | ||
|
|
9ca713dfd9 | ||
|
|
b1b04b8e87 | ||
|
|
851a1f941e | ||
|
|
6b9d92de65 | ||
|
|
d763ad0e60 | ||
|
|
fd72e24a86 | ||
|
|
20077d3d56 | ||
|
|
fb798d6ce7 | ||
|
|
a21ba820e9 | ||
|
|
792c5e569a | ||
|
|
f92192efd4 | ||
|
|
1f50c7ccc5 | ||
|
|
8282460774 | ||
|
|
a888502038 | ||
|
|
ea07642172 | ||
|
|
e9da037001 | ||
|
|
7ee50a3f8f | ||
|
|
c16258d4d1 | ||
|
|
c7848c9c02 | ||
|
|
5341d7a5fd | ||
|
|
402e1f0f25 | ||
|
|
8395df633c | ||
|
|
19471275df | ||
|
|
e75f5661e2 | ||
|
|
6554063df3 | ||
|
|
a034fc16d6 | ||
|
|
62fbd422e2 | ||
|
|
4416a380b3 | ||
|
|
d0bae6aff4 | ||
|
|
8a7bb7f2ca | ||
|
|
46394b2000 | ||
|
|
0df6c83df5 | ||
|
|
8996fe03dc | ||
|
|
3fe3bc4cfa | ||
|
|
f3cebd9099 | ||
|
|
6661910672 | ||
|
|
920c6e6928 | ||
|
|
e7a36630f6 | ||
|
|
69780aa048 | ||
|
|
f32b0b34a3 | ||
|
|
2a0ebfde93 | ||
|
|
6e9e6abe98 | ||
|
|
6b731ddd7a | ||
|
|
b9769b9f55 | ||
|
|
7e42483986 | ||
|
|
97b0d08e4f | ||
|
|
99e35d8b11 | ||
|
|
d2c356f9e2 | ||
|
|
5e99fd5ef9 | ||
|
|
65b542f0ab | ||
|
|
04dd4a64d5 | ||
|
|
b826870be9 | ||
|
|
50b0e9cb65 | ||
|
|
ed5276d67b | ||
|
|
82be18ec4b | ||
|
|
4ee3a1464b | ||
|
|
69c9d85be7 | ||
|
|
8e215d3b66 | ||
|
|
c45d51e950 | ||
|
|
ad05d059c7 | ||
|
|
85eb34c01e | ||
|
|
8a24c6c3ca | ||
|
|
46cdf48bc4 | ||
|
|
9d542b92b0 | ||
|
|
1b2cd1497c | ||
|
|
71ad228d8d | ||
|
|
339a0d5d4e | ||
|
|
9cebb76787 | ||
|
|
d9d5a46c6e | ||
|
|
251622c8bc | ||
|
|
d73b711e3a | ||
|
|
18739235a9 | ||
|
|
07c493c796 | ||
|
|
cca0e4b3f3 | ||
|
|
1428580109 | ||
|
|
fb0e1d10b0 | ||
|
|
6f81e1d294 | ||
|
|
cec6d89474 |
@@ -12,3 +12,5 @@ scripts/config.status
|
||||
*~
|
||||
*.log
|
||||
UPDATE_ME
|
||||
tcltk/netgen.sh
|
||||
tcltk/netgen.tcl
|
||||
|
||||
+330
-208
@@ -193,17 +193,19 @@ void flattenCell(char *name, int file)
|
||||
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
|
||||
FreeString(tmp->name);
|
||||
tmp->name = strsave(tmpstr);
|
||||
#if OLDPREFIX
|
||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||
tmp->instance.name);
|
||||
#else
|
||||
strcpy(tmpstr+prefixlength,tmp->instance.name);
|
||||
#endif
|
||||
FreeString(tmp->instance.name);
|
||||
tmp->instance.name = strsave(tmpstr);
|
||||
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||
if (tmp->type == FIRSTPIN)
|
||||
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
|
||||
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
|
||||
#if OLDPREFIX
|
||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||
tmp->instance.name);
|
||||
#else
|
||||
strcpy(tmpstr+prefixlength,tmp->instance.name);
|
||||
#endif
|
||||
FreeString(tmp->instance.name);
|
||||
tmp->instance.name = strsave(tmpstr);
|
||||
if (tmp->type == FIRSTPIN)
|
||||
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
|
||||
}
|
||||
}
|
||||
|
||||
/* splice instance out of parent */
|
||||
@@ -252,9 +254,10 @@ void flattenCell(char *name, int file)
|
||||
int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||
{
|
||||
struct objlist *ParentParams;
|
||||
struct objlist *ParentProps;
|
||||
struct objlist *NextObj;
|
||||
struct objlist *ChildObjList;
|
||||
struct objlist *ParentProps, *CurrentProp;
|
||||
struct objlist *NextObj, *LastObj, *prepp;
|
||||
struct objlist *ChildObjList, *ChildListEnd;
|
||||
struct objlist *ChildStart, *ChildEnd, *ParentEnd, *ParentNext;
|
||||
struct nlist *ThisCell;
|
||||
struct nlist *ChildCell;
|
||||
struct objlist *tmp, *ob2, *ob3;
|
||||
@@ -296,21 +299,35 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||
while (notdone) {
|
||||
notdone = 0;
|
||||
ParentParams = ThisCell->cell;
|
||||
LastObj = NULL;
|
||||
|
||||
for (ParentParams = ThisCell->cell; ParentParams != NULL;
|
||||
ParentParams = NextObj) {
|
||||
ParentParams = NextObj) {
|
||||
if (Debug) Printf("Parent = %s, type = %d\n",
|
||||
ParentParams->name, ParentParams->type);
|
||||
NextObj = ParentParams->next;
|
||||
if (ParentParams->type != FIRSTPIN) continue;
|
||||
if (!(*matchfunc)(ParentParams->model.class, instance)) continue;
|
||||
if (ParentParams->type != FIRSTPIN) {
|
||||
LastObj = ParentParams;
|
||||
continue;
|
||||
}
|
||||
if (!(*matchfunc)(ParentParams->model.class, instance)) {
|
||||
LastObj = ParentParams;
|
||||
continue;
|
||||
}
|
||||
|
||||
ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file);
|
||||
if (Debug)
|
||||
Printf(" Flattening instance: %s, primitive = %s\n",
|
||||
ParentParams->instance.name, (ChildCell->class ==
|
||||
CLASS_SUBCKT) ? "no" : "yes");
|
||||
if (ChildCell->class != CLASS_SUBCKT) continue;
|
||||
if (ChildCell == ThisCell) continue; // Avoid infinite loop
|
||||
if ((ChildCell->class != CLASS_SUBCKT) && (ChildCell->class != CLASS_MODULE)) {
|
||||
LastObj = ParentParams;
|
||||
continue;
|
||||
}
|
||||
if (ChildCell == ThisCell) {
|
||||
LastObj = ParentParams;
|
||||
continue; // Avoid infinite loop
|
||||
}
|
||||
|
||||
/* Does the parent cell have properties? If so, save a pointer to them */
|
||||
for (ParentProps = ParentParams->next; ParentProps &&
|
||||
@@ -318,202 +335,264 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||
ParentProps = ParentProps->next) {
|
||||
if (ParentProps->type == PROPERTY) break;
|
||||
}
|
||||
if (ParentProps && (ParentProps->type != PROPERTY)) ParentProps = NULL;
|
||||
|
||||
/* not primitive, so need to flatten this instance */
|
||||
notdone = 1;
|
||||
/* if this is a new instance, flatten it */
|
||||
/* if (ChildCell->dumped == 0) flattenCell(ParentParams->model.class, file); */
|
||||
|
||||
ChildObjList = CopyObjList(ChildCell->cell, 1);
|
||||
numflat++;
|
||||
|
||||
/* update node numbers in child to unique numbers */
|
||||
oldmax = 0;
|
||||
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
|
||||
if (tmp->node > oldmax) oldmax = tmp->node;
|
||||
if (nextnode <= oldmax) nextnode = oldmax + 1;
|
||||
|
||||
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
|
||||
if (tmp->node <= oldmax && tmp->node > 0) {
|
||||
if (Debug) Printf("Update node %d --> %d\n", tmp->node, nextnode);
|
||||
UpdateNodeNumbers(ChildObjList, tmp->node, nextnode);
|
||||
nextnode++;
|
||||
}
|
||||
|
||||
/* copy nodenumbers of ports from parent */
|
||||
ob2 = ParentParams;
|
||||
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
|
||||
if (IsPort(tmp)) {
|
||||
if (tmp->node > 0) {
|
||||
if (ob2->node == -1) {
|
||||
|
||||
// Before commiting to attaching to a unconnected node, see
|
||||
// if there is another node in ParentParams with the same
|
||||
// name and a valid node number. If so, connect them. In
|
||||
// the broader case, it may be necessary to consider all
|
||||
// nodes, not just those with node == -1, and call join()
|
||||
// here to update all node numbers in the parent cell.
|
||||
// In that case, a more efficient method is needed for
|
||||
// tracking same-name ports.
|
||||
|
||||
for (ob3 = ParentParams; ob3 && ob3->type >= FIRSTPIN; ob3 = ob3->next) {
|
||||
if (ob3 == ob2) continue;
|
||||
if ((*matchfunc)(ob3->name, ob2->name) && ob3->node != -1) {
|
||||
ob2->node = ob3->node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Debug) {
|
||||
// Printf(" Sealing port: %d to node %d\n", tmp->node, ob2->node);
|
||||
Printf("Update node %d --> %d\n", tmp->node, ob2->node);
|
||||
}
|
||||
UpdateNodeNumbers(ChildObjList, tmp->node, ob2->node);
|
||||
}
|
||||
|
||||
/* in pathological cases, the lengths of the port lists may
|
||||
change. This is an error, but that is no reason to allow
|
||||
the code to core dump. We avoid this by placing a
|
||||
superfluous check on ob2->type
|
||||
*/
|
||||
|
||||
if (ob2 != NULL)
|
||||
ob2 = ob2->next;
|
||||
|
||||
if (ob2 == NULL) break;
|
||||
}
|
||||
|
||||
/* Using name == NULL to indicate that a .ext file is being */
|
||||
/* flattened on the fly. This is quick & dirty. */
|
||||
|
||||
if (name != NULL) {
|
||||
/* delete all port elements from child */
|
||||
while ((ChildObjList != NULL) && IsPort(ChildObjList)) {
|
||||
/* delete all ports at beginning of list */
|
||||
if (Debug) Printf("deleting leading port from child\n");
|
||||
tmp = ChildObjList->next;
|
||||
// FreeObjectAndHash(ChildObjList, ChildCell);
|
||||
FreeObject(ChildObjList);
|
||||
ChildObjList = tmp;
|
||||
}
|
||||
tmp = ChildObjList;
|
||||
while (tmp && (tmp->next != NULL)) {
|
||||
if (IsPort(tmp->next)) {
|
||||
ob2 = (tmp->next)->next;
|
||||
if (Debug) Printf("deleting a port from child\n");
|
||||
// FreeObjectAndHash(tmp->next, ChildCell);
|
||||
FreeObject(tmp->next);
|
||||
tmp->next = ob2;
|
||||
}
|
||||
else tmp = tmp->next;
|
||||
}
|
||||
if (ParentProps && (ParentProps->type != PROPERTY)) {
|
||||
ParentProps = NULL;
|
||||
CurrentProp = NULL;
|
||||
}
|
||||
else
|
||||
CurrentProp = ParentProps;
|
||||
|
||||
/* for each element in child, prepend 'prefix' */
|
||||
/* Find the end record of the parent cell and save it */
|
||||
for (ParentEnd = (ParentProps) ? ParentProps : ParentParams;
|
||||
ParentEnd && ParentEnd->next &&
|
||||
/* Stop on a node or the next instance */
|
||||
((ParentEnd->next->type > FIRSTPIN) ||
|
||||
(ParentEnd->next->type == PROPERTY));
|
||||
ParentEnd = ParentEnd->next);
|
||||
|
||||
/* Not primitive, so need to flatten this instance */
|
||||
notdone = 1;
|
||||
|
||||
/* Loop over property records. Need to flatten once per */
|
||||
/* property record (or more, if property has M > 1) */
|
||||
|
||||
ob2 = NULL;
|
||||
ChildObjList = NULL;
|
||||
ChildListEnd = NULL;
|
||||
while (1) {
|
||||
|
||||
ChildStart = CopyObjList(ChildCell->cell, 1);
|
||||
numflat++;
|
||||
|
||||
/* Find the end record of the child cell and save it */
|
||||
for (ChildEnd = ChildStart; ChildEnd && ChildEnd->next;
|
||||
ChildEnd = ChildEnd->next);
|
||||
|
||||
if (ChildListEnd == NULL) ChildListEnd = ChildEnd;
|
||||
|
||||
/* update node numbers in child to unique numbers */
|
||||
oldmax = 0;
|
||||
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next)
|
||||
if (tmp->node > oldmax) oldmax = tmp->node;
|
||||
if (nextnode <= oldmax) nextnode = oldmax + 1;
|
||||
|
||||
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next)
|
||||
if (tmp->node <= oldmax && tmp->node > 0) {
|
||||
if (Debug) Printf("Update node %d --> %d\n", tmp->node, nextnode);
|
||||
UpdateNodeNumbers(ChildStart, tmp->node, nextnode);
|
||||
nextnode++;
|
||||
}
|
||||
|
||||
/* copy nodenumbers of ports from parent */
|
||||
ob2 = ParentParams;
|
||||
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) {
|
||||
if (IsPort(tmp)) {
|
||||
if (tmp->node > 0) {
|
||||
if (ob2->node == -1) {
|
||||
|
||||
// Before commiting to attaching to a unconnected node, see
|
||||
// if there is another node in ParentParams with the same
|
||||
// name and a valid node number. If so, connect them. In
|
||||
// the broader case, it may be necessary to consider all
|
||||
// nodes, not just those with node == -1, and call join()
|
||||
// here to update all node numbers in the parent cell.
|
||||
// In that case, a more efficient method is needed for
|
||||
// tracking same-name ports.
|
||||
|
||||
for (ob3 = ParentParams; ob3 && ob3->type >= FIRSTPIN;
|
||||
ob3 = ob3->next) {
|
||||
if (ob3 == ob2) continue;
|
||||
if ((*matchfunc)(ob3->name, ob2->name) && ob3->node != -1) {
|
||||
ob2->node = ob3->node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Debug) {
|
||||
// Printf(" Sealing port: %d to node %d\n", tmp->node, ob2->node);
|
||||
Printf("Update node %d --> %d\n", tmp->node, ob2->node);
|
||||
}
|
||||
UpdateNodeNumbers(ChildStart, tmp->node, ob2->node);
|
||||
}
|
||||
|
||||
/* in pathological cases, the lengths of the port lists may
|
||||
* change. This is an error, but that is no reason to allow
|
||||
* the code to core dump. We avoid this by placing a
|
||||
* superfluous check on ob2->type
|
||||
*/
|
||||
if (ob2 != NULL) ob2 = ob2->next;
|
||||
|
||||
if (ob2 == NULL) break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Using name == NULL to indicate that a .ext file is being */
|
||||
/* flattened on the fly. This is quick & dirty. */
|
||||
|
||||
if (name != NULL) {
|
||||
/* delete all port elements from child */
|
||||
while ((ChildStart != NULL) && IsPort(ChildStart)) {
|
||||
/* delete all ports at beginning of list */
|
||||
if (Debug) Printf("deleting leading port from child\n");
|
||||
tmp = ChildStart->next;
|
||||
FreeObject(ChildStart);
|
||||
ChildStart = tmp;
|
||||
}
|
||||
tmp = ChildStart;
|
||||
while (tmp && (tmp->next != NULL)) {
|
||||
if (IsPort(tmp->next)) {
|
||||
ob2 = (tmp->next)->next;
|
||||
if (Debug) Printf("deleting a port from child\n");
|
||||
FreeObject(tmp->next);
|
||||
tmp->next = ob2;
|
||||
}
|
||||
else tmp = tmp->next;
|
||||
}
|
||||
if (ChildStart == NULL) {
|
||||
if (ChildListEnd == ChildEnd) ChildListEnd = NULL;
|
||||
ChildEnd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* for each element in child, prepend 'prefix' */
|
||||
#if !OLDPREFIX
|
||||
/* replaces all the sprintf's below */
|
||||
strcpy(tmpstr,ParentParams->instance.name);
|
||||
strcat(tmpstr,SEPARATOR);
|
||||
prefixlength = strlen(tmpstr);
|
||||
/* replaces all the sprintf's below */
|
||||
strcpy(tmpstr,ParentParams->instance.name);
|
||||
strcat(tmpstr,SEPARATOR);
|
||||
prefixlength = strlen(tmpstr);
|
||||
#endif
|
||||
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) {
|
||||
if (tmp->type == PROPERTY)
|
||||
continue;
|
||||
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) {
|
||||
if (tmp->type == PROPERTY)
|
||||
continue;
|
||||
|
||||
else if (IsGlobal(tmp)) {
|
||||
/* Keep the name but search for node of same name in parent */
|
||||
/* and replace the node number, if found. */
|
||||
else if (IsGlobal(tmp)) {
|
||||
/* Keep the name but search for node of same name in parent */
|
||||
/* and replace the node number, if found. */
|
||||
|
||||
for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) {
|
||||
/* Type in parent may be a port, not a global */
|
||||
if (ob2->type == tmp->type || ob2->type == PORT) {
|
||||
if ((*matchfunc)(tmp->name, ob2->name)) {
|
||||
if (ob2->node >= 0) {
|
||||
// Replace all child objects with this node number
|
||||
rnodenum = tmp->node;
|
||||
for (ob3 = ChildObjList; ob3 != NULL; ob3 = ob3->next) {
|
||||
if (ob3->node == rnodenum)
|
||||
ob3->node = ob2->node;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't hash this if the parent had a port of this name
|
||||
if (!ob2 || ob2->type != PORT)
|
||||
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||
continue;
|
||||
}
|
||||
for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) {
|
||||
/* Type in parent may be a port, not a global */
|
||||
if (ob2->type == tmp->type || ob2->type == PORT) {
|
||||
if ((*matchfunc)(tmp->name, ob2->name)) {
|
||||
if (ob2->node >= 0) {
|
||||
// Replace all child objects with this node number
|
||||
rnodenum = tmp->node;
|
||||
for (ob3 = ChildStart; ob3 != NULL; ob3 = ob3->next) {
|
||||
if (ob3->node == rnodenum)
|
||||
ob3->node = ob2->node;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't hash this if the parent had a port of this name
|
||||
if (!ob2 || ob2->type != PORT)
|
||||
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||
continue;
|
||||
}
|
||||
|
||||
#if OLDPREFIX
|
||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||
tmp->name);
|
||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||
tmp->name);
|
||||
#else
|
||||
strcpy(tmpstr+prefixlength,tmp->name);
|
||||
strcpy(tmpstr+prefixlength,tmp->name);
|
||||
#endif
|
||||
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
|
||||
FreeString(tmp->name);
|
||||
tmp->name = strsave(tmpstr);
|
||||
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
|
||||
FreeString(tmp->name);
|
||||
tmp->name = strsave(tmpstr);
|
||||
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
|
||||
#if OLDPREFIX
|
||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||
tmp->instance.name);
|
||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||
tmp->instance.name);
|
||||
#else
|
||||
strcpy(tmpstr+prefixlength,tmp->instance.name);
|
||||
strcpy(tmpstr+prefixlength,tmp->instance.name);
|
||||
#endif
|
||||
FreeString(tmp->instance.name);
|
||||
tmp->instance.name = strsave(tmpstr);
|
||||
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||
if (tmp->type == FIRSTPIN)
|
||||
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
|
||||
}
|
||||
|
||||
/* Do property inheritance */
|
||||
|
||||
if (ParentProps) {
|
||||
for (ob2 = ChildObjList; ob2 != NULL; ob2=ob2->next) {
|
||||
|
||||
/* If the parent cell has properties to declare, then */
|
||||
/* pass them on to children. Use globals only if the */
|
||||
/* spiceparams dictionary is active (during file reading */
|
||||
/* only). */
|
||||
|
||||
if (ob2->type == PROPERTY)
|
||||
ReduceExpressions(ob2, ParentProps, ChildCell,
|
||||
(spiceparams.hashtab == NULL) ? 0 : 1);
|
||||
FreeString(tmp->instance.name);
|
||||
tmp->instance.name = strsave(tmpstr);
|
||||
if (tmp->type == FIRSTPIN)
|
||||
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
|
||||
}
|
||||
}
|
||||
|
||||
/* Do property inheritance */
|
||||
|
||||
/* NOTE: Need to do: Check properties for M > 1 and decrement
|
||||
* and repeat without moving CurrentProp
|
||||
*/
|
||||
|
||||
if (CurrentProp) {
|
||||
for (ob2 = ChildStart; ob2 != NULL; ob2=ob2->next) {
|
||||
|
||||
/* If the parent cell has properties to declare, then */
|
||||
/* pass them on to children. Use globals only if the */
|
||||
/* spiceparams dictionary is active (during file */
|
||||
/* reading only). */
|
||||
|
||||
if (ob2->type == PROPERTY)
|
||||
ReduceExpressions(ob2, CurrentProp, ChildCell,
|
||||
(spiceparams.hashtab == NULL) ? 0 : 1);
|
||||
}
|
||||
|
||||
/* Repeat for each property record, as each property represents a
|
||||
* unique instance that must be flattened individually.
|
||||
*/
|
||||
|
||||
CurrentProp = CurrentProp->next;
|
||||
if ((CurrentProp == NULL) || (CurrentProp->type != PROPERTY)) break;
|
||||
}
|
||||
else break;
|
||||
|
||||
/* Put the child cell at the start of ChildObjList */
|
||||
ChildEnd->next = ChildObjList;
|
||||
ChildObjList = ChildStart;
|
||||
}
|
||||
|
||||
/* splice instance out of parent */
|
||||
if ((ParentParams == ThisCell->cell) && (ChildObjList == NULL)) {
|
||||
ThisCell->cell = ob2; /* Child cell was empty */
|
||||
tmp = ob2;
|
||||
/* Put the child cell at the start of ChildObjList */
|
||||
if (ChildEnd) {
|
||||
ChildEnd->next = ChildObjList;
|
||||
ChildObjList = ChildStart;
|
||||
}
|
||||
else {
|
||||
|
||||
/* Pull the instance out of the parent */
|
||||
|
||||
if ((ParentParams != ThisCell->cell) || (ChildObjList != NULL)) {
|
||||
if (ParentParams == ThisCell->cell) {
|
||||
/* ParentParams are the very first thing in the list */
|
||||
ThisCell->cell = ChildObjList;
|
||||
for (ob2 = ChildObjList; ob2 && ob2->next != NULL; ob2 = ob2->next) ;
|
||||
/* ParentParams are the very first thing in the list */
|
||||
ThisCell->cell = ChildObjList;
|
||||
}
|
||||
else {
|
||||
/* find ParentParams in ThisCell list */
|
||||
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams; ob2=ob2->next);
|
||||
if (ob2)
|
||||
for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ;
|
||||
else if (ChildObjList) {
|
||||
/* find ParentParams in ThisCell list. In most cases, LastObj */
|
||||
/* should be pointing to it. */
|
||||
if (LastObj && (LastObj->next == ParentParams)) {
|
||||
LastObj->next = ChildObjList;
|
||||
}
|
||||
else {
|
||||
for (ob2 = LastObj; ob2 && ob2->next != ParentParams;
|
||||
ob2 = ob2->next);
|
||||
if (ob2 == NULL) {
|
||||
/* It should not happen that LastObj is ahead of ParentParams */
|
||||
/* but just in case, this long loop will find it. */
|
||||
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams;
|
||||
ob2 = ob2->next);
|
||||
}
|
||||
ob2->next = ChildObjList;
|
||||
}
|
||||
}
|
||||
/* now, ob2 is last element in child list, so skip and reclaim parent */
|
||||
else {
|
||||
/* The child was completely optimized out, so close list around it */
|
||||
LastObj->next = ParentEnd->next;
|
||||
}
|
||||
|
||||
tmp = ParentParams;
|
||||
do {
|
||||
tmp = tmp->next;
|
||||
} while ((tmp != NULL) && ((tmp->type > FIRSTPIN) || (tmp->type == PROPERTY)));
|
||||
if (ob2) ob2->next = tmp;
|
||||
/* Link end of child list into the parent */
|
||||
if (ChildListEnd && ParentEnd)
|
||||
ChildListEnd->next = ParentEnd->next;
|
||||
}
|
||||
while (ParentParams != tmp) {
|
||||
ob2 = ParentParams->next;
|
||||
FreeObjectAndHash(ParentParams, ThisCell);
|
||||
ParentParams = ob2;
|
||||
ParentNext = (ParentEnd) ? ParentEnd->next : NULL;
|
||||
while (ParentParams != ParentNext) {
|
||||
ob2 = ParentParams->next;
|
||||
FreeObjectAndHash(ParentParams, ThisCell);
|
||||
ParentParams = ob2;
|
||||
}
|
||||
NextObj = ParentParams;
|
||||
} /* repeat until no more instances found */
|
||||
@@ -1136,9 +1215,10 @@ int UniquePins(char *name, int filenum)
|
||||
struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
||||
{
|
||||
struct nlist *ptr;
|
||||
struct objlist *ob, *obt, *lob, *nob, *firstpin;
|
||||
struct objlist *ob, *obt, *lob, *nob, *firstpin, *pob;
|
||||
struct nlist *tc = (struct nlist *)clientdata;
|
||||
int pinnum;
|
||||
char *saveinst = NULL;
|
||||
|
||||
ptr = (struct nlist *)(p->ptr);
|
||||
if (tc->file != ptr->file) return NULL;
|
||||
@@ -1186,13 +1266,44 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
||||
}
|
||||
|
||||
FREE(ob->name);
|
||||
if (ob->instance.name != NULL) FREE(ob->instance.name);
|
||||
if (ob->instance.name != NULL) {
|
||||
/* Keep a copy of the instance name (see below) */
|
||||
if (saveinst != NULL) FREE(saveinst);
|
||||
saveinst = ob->instance.name;
|
||||
}
|
||||
if (ob->model.class != NULL) FREE(ob->model.class);
|
||||
FREE(ob);
|
||||
}
|
||||
else {
|
||||
lob = ob;
|
||||
ob->type = pinnum++; // Renumber pins in order
|
||||
if ((ob->type == PROPERTY) && (pinnum == 1))
|
||||
{
|
||||
/* If this happens, then all the pins got removed,
|
||||
* and there is probably something very much wrong
|
||||
* with the setup. However, to keep netgen from
|
||||
* blowing up, add back a "proxy(no pins)" record
|
||||
* in front; otherwise we'd have an orphaned
|
||||
* property record.
|
||||
*/
|
||||
pob = GetObject();
|
||||
pob->name = (char *)MALLOC(15);
|
||||
sprintf(pob->name, "proxy(no pins)");
|
||||
pob->model.class = strsave(ob->model.class);
|
||||
if (saveinst != NULL)
|
||||
pob->instance.name = strsave(saveinst);
|
||||
else
|
||||
/* This should never happen */
|
||||
pob->instance.name = strsave("error");
|
||||
pob->type = pinnum++;
|
||||
pob->node = -1;
|
||||
pob->next = ob;
|
||||
lob->next = pob;
|
||||
lob = ob;
|
||||
}
|
||||
else
|
||||
{
|
||||
lob = ob;
|
||||
ob->type = pinnum++; // Renumber pins in order
|
||||
}
|
||||
}
|
||||
ob = nob;
|
||||
obt = obt->next;
|
||||
@@ -1203,6 +1314,8 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
||||
HashPtrInstall(firstpin->instance.name, firstpin, &(ptr->instdict));
|
||||
}
|
||||
}
|
||||
|
||||
if (saveinst != NULL) FREE(saveinst);
|
||||
return NULL; /* Keep the search going */
|
||||
}
|
||||
|
||||
@@ -1475,13 +1588,13 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if (ecomp->cell1) {
|
||||
if (ecomp->cell1 && (ecomp->num1 > 0)) {
|
||||
Fprintf(stdout, "Flattening instances of %s in cell %s"
|
||||
" makes a better match\n", ecomp->cell1->name,
|
||||
name1);
|
||||
flattenInstancesOf(name1, file1, ecomp->cell1->name);
|
||||
}
|
||||
if (ecomp->cell2) {
|
||||
if (ecomp->cell2 && (ecomp->num2 > 0)) {
|
||||
Fprintf(stdout, "Flattening instances of %s in cell %s"
|
||||
" makes a better match\n", ecomp->cell2->name,
|
||||
name2);
|
||||
@@ -1660,7 +1773,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
while (ecomp != NULL) {
|
||||
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
|
||||
((ecomp->cell1->class == CLASS_RES) ||
|
||||
(ecomp->cell1->class == CLASS_VSOURCE))) {
|
||||
(ecomp->cell1->class == CLASS_VSOURCE) ||
|
||||
(ecomp->cell1->class == CLASS_ISOURCE))) {
|
||||
int node1 = -1, node2 = -1;
|
||||
lob = NULL;
|
||||
for (ob1 = tc1->cell; ob1; ) {
|
||||
@@ -1709,11 +1823,16 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
tsub1->name,
|
||||
tc1->name);
|
||||
|
||||
/* merge node of endpoints */
|
||||
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
|
||||
if (ob2->node == node2)
|
||||
ob2->node = node1;
|
||||
}
|
||||
/* A current source is an open, while a */
|
||||
/* resistor or voltage source is a short. */
|
||||
|
||||
if (ecomp->cell1->class != CLASS_ISOURCE) {
|
||||
/* merge node of endpoints */
|
||||
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
|
||||
if (ob2->node == node2)
|
||||
ob2->node = node1;
|
||||
}
|
||||
}
|
||||
|
||||
/* snip, snip. Excise this device */
|
||||
if (lob == NULL) {
|
||||
@@ -1765,7 +1884,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
|
||||
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) &&
|
||||
((ecomp->cell2->class == CLASS_RES) ||
|
||||
(ecomp->cell2->class == CLASS_VSOURCE))) {
|
||||
(ecomp->cell2->class == CLASS_VSOURCE) ||
|
||||
(ecomp->cell2->class == CLASS_ISOURCE))) {
|
||||
int node1 = -1, node2 = -1;
|
||||
lob = NULL;
|
||||
for (ob2 = tc2->cell; ob2; ) {
|
||||
@@ -1815,9 +1935,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
||||
tc2->name);
|
||||
|
||||
/* merge node of endpoints */
|
||||
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
|
||||
if (ob1->node == node2)
|
||||
ob1->node = node1;
|
||||
if (ecomp->cell2->class != CLASS_ISOURCE) {
|
||||
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
|
||||
if (ob1->node == node2)
|
||||
ob1->node = node1;
|
||||
}
|
||||
}
|
||||
|
||||
/* snip, snip. Excise this device */
|
||||
|
||||
+830
-264
File diff suppressed because it is too large
Load Diff
+44
-5
@@ -289,6 +289,7 @@ int GetNextLineNoNewline(char *delimiter)
|
||||
char *newbuf;
|
||||
int testc;
|
||||
int nested = 0;
|
||||
int llen;
|
||||
|
||||
if (feof(infile)) return -1;
|
||||
|
||||
@@ -306,12 +307,25 @@ int GetNextLineNoNewline(char *delimiter)
|
||||
linetok = (char *)MALLOC(linesize + 1);
|
||||
}
|
||||
fgets(line, linesize, infile);
|
||||
while (strlen(line) == linesize - 1) {
|
||||
/* Immediately resolve backslash-EOL */
|
||||
llen = strlen(line);
|
||||
while ((llen > 1) && (llen < linesize - 1) && *(line + llen - 2) == '\\') {
|
||||
*(line + llen - 2) = '\n';
|
||||
fgets(line + llen - 1, linesize - llen + 1, infile);
|
||||
llen = strlen(line);
|
||||
}
|
||||
while (llen == linesize - 1) {
|
||||
newbuf = (char *)MALLOC(linesize + 501);
|
||||
strcpy(newbuf, line);
|
||||
FREE(line);
|
||||
line = newbuf;
|
||||
fgets(line + linesize - 1, 501, infile);
|
||||
llen = strlen(line);
|
||||
while ((llen > 1) && (llen < linesize - 1) && *(line + llen - 2) == '\\') {
|
||||
*(line + llen - 2) = '\n';
|
||||
fgets(line + llen - 1, linesize - llen + 1, infile);
|
||||
llen = strlen(line);
|
||||
}
|
||||
linesize += 500;
|
||||
FREE(linetok);
|
||||
linetok = (char *)MALLOC(linesize + 1);
|
||||
@@ -329,7 +343,7 @@ int GetNextLineNoNewline(char *delimiter)
|
||||
for (s = line; *s != '\0'; s++) {
|
||||
if (*s == '`') {
|
||||
w = s + 1;
|
||||
while (isalnum(*w)) w++;
|
||||
while (isalnum(*w) || (*w == '_') || (*w == '$')) w++;
|
||||
e = *w;
|
||||
*w = '\0';
|
||||
kl = (struct property *)HashLookup(s + 1, definitions);
|
||||
@@ -365,7 +379,7 @@ int GetNextLineNoNewline(char *delimiter)
|
||||
for (s = line; *s != '\0'; s++) {
|
||||
if (*s == '`') {
|
||||
w = s + 1;
|
||||
while (isalnum(*w)) w++;
|
||||
while (isalnum(*w) || (*w == '_') || (*w == '$')) w++;
|
||||
e = *w;
|
||||
*w = '\0';
|
||||
kl = (struct property *)HashLookup(s + 1, definitions);
|
||||
@@ -496,6 +510,24 @@ void GetNextLine(char *delimiter)
|
||||
} while (nexttok == NULL);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Return a pointer to the line at the position of nexttok */
|
||||
/* This is used only when returning the entire line, untokenized, and */
|
||||
/* is only called by the verilog read routine when reading the value of */
|
||||
/* a `define statement. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
char *GetLineAtTok()
|
||||
{
|
||||
char *lpos;
|
||||
|
||||
if (nexttok == NULL) return NULL;
|
||||
if (line == NULL) return NULL;
|
||||
|
||||
lpos = strstr(line, nexttok);
|
||||
return lpos;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* if nexttok is already NULL, force scanner to read new line */
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -766,7 +798,7 @@ int OpenParseFile(char *name, int fnum)
|
||||
FILE *locfile;
|
||||
struct filestack *newfile;
|
||||
|
||||
locfile = fopen(name,"r");
|
||||
locfile = fopen(name, "r");
|
||||
linenum = 0;
|
||||
/* reset the token scanner */
|
||||
nexttok = NULL;
|
||||
@@ -851,7 +883,12 @@ char *ReadNetlist(char *fname, int *fnum)
|
||||
{SPICE_EXTENSION, ReadSpice},
|
||||
{SPICE_EXT2, ReadSpice},
|
||||
{SPICE_EXT3, ReadSpice},
|
||||
{SPICE_EXT4, ReadSpice},
|
||||
{SPICE_EXT5, ReadSpice},
|
||||
{SPICE_EXT6, ReadSpice},
|
||||
{SPICE_EXT7, ReadSpice},
|
||||
{VERILOG_EXTENSION, ReadVerilog},
|
||||
{SYS_VERILOG_EXTENSION, ReadVerilog},
|
||||
{NETGEN_EXTENSION, ReadNetgenFile},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@@ -859,7 +896,9 @@ char *ReadNetlist(char *fname, int *fnum)
|
||||
|
||||
/* make first pass looking for extension */
|
||||
for (index = 0; formats[index].extension != NULL; index++) {
|
||||
if (strstr(fname, formats[index].extension) != NULL) {
|
||||
int extlen = strlen(formats[index].extension);
|
||||
int flen = strlen(fname);
|
||||
if (!strcmp(fname + flen - extlen, formats[index].extension)) {
|
||||
return (*(formats[index].proc))(fname, fnum);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -9,11 +9,16 @@
|
||||
#define SIM_EXTENSION ".sim"
|
||||
#define SPICE_EXTENSION ".spice"
|
||||
#define SPICE_EXT2 ".spc"
|
||||
#define SPICE_EXT3 ".fspc"
|
||||
#define SPICE_EXT3 ".sp"
|
||||
#define SPICE_EXT4 ".spi"
|
||||
#define SPICE_EXT5 ".fspc"
|
||||
#define SPICE_EXT6 ".cir"
|
||||
#define SPICE_EXT7 ".ckt"
|
||||
#define NETGEN_EXTENSION ".ntg"
|
||||
#define CCODE_EXTENSION ".c.code"
|
||||
#define ESACAP_EXTENSION ".esa"
|
||||
#define VERILOG_EXTENSION ".v"
|
||||
#define SYS_VERILOG_EXTENSION ".sv"
|
||||
|
||||
#define LINELENGTH 80
|
||||
|
||||
@@ -31,6 +36,7 @@ extern struct hashdict *definitions;
|
||||
extern char *nexttok;
|
||||
#define SKIPTO(a) do {SkipTok(NULL);} while (!match(nexttok,a))
|
||||
extern char *strdtok(char *pstring, char *delim1, char *delim2);
|
||||
extern char *GetLineAtTok();
|
||||
extern void SkipTok(char *delimiter);
|
||||
extern void SkipTokNoNewline(char *delimiter);
|
||||
extern void SkipTokComments(char *delimiter);
|
||||
|
||||
+621
-532
File diff suppressed because it is too large
Load Diff
+12
-1
@@ -43,7 +43,8 @@ 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);
|
||||
extern void CopyProperties(struct objlist *obj_to, struct objlist *obj_from);
|
||||
extern int PromoteProperty(struct property *, struct valuelist *);
|
||||
extern int PromoteProperty(struct property *, struct valuelist *,
|
||||
struct objlist *, struct nlist *);
|
||||
extern int SetPropertyDefault(struct property *, struct valuelist *);
|
||||
extern struct objlist *LinkProperties(char *model, struct keyvalue *topptr);
|
||||
extern int ReduceExpressions(struct objlist *instprop, struct objlist *parprops,
|
||||
@@ -65,6 +66,15 @@ extern void AssignCircuits(char *name1, int file1, char *name2, int file2);
|
||||
/* flatten.c */
|
||||
extern int PrematchLists(char *, int, char *, int);
|
||||
|
||||
/* verilog.c */
|
||||
struct cellstack {
|
||||
char *cellname;
|
||||
struct cellstack *next;
|
||||
};
|
||||
|
||||
void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
||||
int blackbox);
|
||||
|
||||
/* Define (enumerate) various device classes, largely based on SPICE */
|
||||
/* model types, mixed with some ext/sim types. */
|
||||
|
||||
@@ -140,6 +150,7 @@ extern int NoOutput; /* set this to 1 to disable stdout output */
|
||||
extern int Composition; /* direction of composition */
|
||||
extern int UnixWildcards; /* TRUE if *,?,{},[] only; false if full REGEXP */
|
||||
extern int GlobalParallelNone; /* If TRUE, don't parallel combine any cells */
|
||||
extern int GlobalParallelOpen; /* If TRUE, parallel combine cells w/no-connects */
|
||||
/* magic internal flag to restrict searches to recently placed cells */
|
||||
extern int QuickSearch;
|
||||
/* does re"CellDef"ing a cell add to it or overwrite it??? */
|
||||
|
||||
+8
-3
@@ -289,7 +289,7 @@ static struct hashdict cell_dict;
|
||||
void InitCellHashTable(void)
|
||||
{
|
||||
hashfunc = hash;
|
||||
matchfunc = match;
|
||||
matchfunc = NULL;
|
||||
matchintfunc = matchfile;
|
||||
InitializeHashTable(&cell_dict, CELLHASHSIZE);
|
||||
}
|
||||
@@ -975,6 +975,7 @@ struct objlist *CopyObjList(struct objlist *oldlist, unsigned char doforall)
|
||||
newob->model.class = NULL;
|
||||
else
|
||||
newob->model.class = strsave(tmp->model.class);
|
||||
newob->flags = tmp->flags;
|
||||
newob->instance.name = (tmp->instance.name) ?
|
||||
strsave(tmp->instance.name) : NULL;
|
||||
}
|
||||
@@ -1112,13 +1113,17 @@ void FreeObjectAndHash(struct objlist *ob, struct nlist *ptr)
|
||||
|
||||
/*************** GENERAL UTILITIES ****************************/
|
||||
|
||||
int NumberOfPorts(char *cellname)
|
||||
int NumberOfPorts(char *cellname, int file)
|
||||
{
|
||||
struct nlist *tp;
|
||||
struct objlist *ob;
|
||||
int ports;
|
||||
|
||||
tp = LookupCell(cellname);
|
||||
if (file == -1)
|
||||
tp = LookupCell(cellname);
|
||||
else
|
||||
tp = LookupCellFile(cellname, file);
|
||||
|
||||
if (tp == NULL) return(0);
|
||||
ports = 0;
|
||||
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
||||
|
||||
+5
-3
@@ -28,6 +28,8 @@
|
||||
|
||||
#define PROXY (0) /* Used in model.port record of ports */
|
||||
|
||||
#define NO_CONNECT 1 /* Use in object list to flag an isolated net */
|
||||
|
||||
/* Lists of device properties. Order is defined at the time of */
|
||||
/* the cell definition; values are sorted at the time instances */
|
||||
/* are read. */
|
||||
@@ -162,6 +164,7 @@ struct objlist {
|
||||
/* (string) value of property for properties */
|
||||
struct valuelist *props; /* Property record */
|
||||
} instance;
|
||||
unsigned char flags; /* Used by NODE type to flag isolated net */
|
||||
int node; /* the electrical node number of the port/node/pin */
|
||||
struct objlist *next;
|
||||
};
|
||||
@@ -192,9 +195,8 @@ struct Permutation {
|
||||
struct Permutation *next;
|
||||
};
|
||||
|
||||
#define OBJHASHSIZE 997 /* the size of the object and instance hash lists */
|
||||
#define OBJHASHSIZE 42073 /* the size of the object and instance hash lists */
|
||||
/* prime numbers are good choices as hash sizes */
|
||||
/* 101 is a good number for IBMPC */
|
||||
|
||||
/* cell definition for hash table */
|
||||
/* NOTE: "file" must come first for the hash matching by name and file */
|
||||
@@ -242,7 +244,7 @@ extern void FreeObjectAndHash(struct objlist *ob, struct nlist *ptr);
|
||||
extern void FreePorts(char *cellname);
|
||||
extern struct IgnoreList *ClassIgnore;
|
||||
|
||||
extern int NumberOfPorts(char *cellname);
|
||||
extern int NumberOfPorts(char *cellname, int file);
|
||||
extern struct objlist *InstanceNumber(struct nlist *tp, int inst);
|
||||
|
||||
extern struct objlist *List(char *list_template);
|
||||
|
||||
+1
-1
@@ -929,7 +929,7 @@ void ToggleDebug(void)
|
||||
void DescribeCell(char *name, int detail)
|
||||
{
|
||||
Printf("Cell: %s contains %d instances, %d nodes and %d ports\n", name,
|
||||
NumberOfInstances(name), RenumberNodes(name), NumberOfPorts(name));
|
||||
NumberOfInstances(name), RenumberNodes(name), NumberOfPorts(name, -1));
|
||||
PrintEmbeddingTree(stdout,name,detail);
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -801,7 +801,11 @@ void DescribeInstance(char *name, int file)
|
||||
{
|
||||
if (ob->node > nodemax) nodemax = ob->node;
|
||||
else if ((ob->node == -1) && (ob->model.port != PROXY)) {
|
||||
if (!(tp->flags & CELL_PLACEHOLDER))
|
||||
|
||||
/* All black-box modules and placeholders by definition have all */
|
||||
/* disconnected pins, so don't report those. */
|
||||
|
||||
if (!(tp->flags & CELL_PLACEHOLDER) && (tp->class != CLASS_MODULE))
|
||||
{
|
||||
if (disconnectednodes == 0) Fprintf(stderr, "\n");
|
||||
disconnectednodes++;
|
||||
@@ -903,7 +907,7 @@ void PrintLeavesInCell(char *cellname, int filenum)
|
||||
np->dumped = 1;
|
||||
|
||||
if (np->class != CLASS_SUBCKT) {
|
||||
Printf("%s; %d ports; Primitive.\n", cellname, NumberOfPorts(cellname));
|
||||
Printf("%s; %d ports; Primitive.\n", cellname, NumberOfPorts(cellname, filenum));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -916,7 +920,7 @@ void PrintLeavesInCell(char *cellname, int filenum)
|
||||
am_a_leaf = 0;
|
||||
}
|
||||
|
||||
if (am_a_leaf) Printf("%s; %d ports\n", cellname, NumberOfPorts(cellname));
|
||||
if (am_a_leaf) Printf("%s; %d ports\n", cellname, NumberOfPorts(cellname, filenum));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1066,7 +1070,7 @@ void Query(void)
|
||||
ActelLib();
|
||||
break;
|
||||
case 'S':
|
||||
promptstring("Read SPICE (.ckt) file? ", repstr);
|
||||
promptstring("Read SPICE (.spice) file? ", repstr);
|
||||
ReadSpice(repstr, &filenum);
|
||||
break;
|
||||
case 'V':
|
||||
|
||||
+38
-20
@@ -473,15 +473,6 @@ void CleanupSubcell() {
|
||||
SetClass(CLASS_MODULE);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Structure for stacking nested subcircuit definitions */
|
||||
/*------------------------------------------------------*/
|
||||
|
||||
struct cellstack {
|
||||
char *cellname;
|
||||
struct cellstack *next;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Push a subcircuit name onto the stack */
|
||||
/*------------------------------------------------------*/
|
||||
@@ -874,6 +865,17 @@ skip_ends:
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore anything in a .CONTROL ... .ENDC block */
|
||||
else if (matchnocase(nexttok, ".CONTROL")) {
|
||||
while (1) {
|
||||
SpiceSkipNewLine();
|
||||
SkipTok(NULL);
|
||||
if (EndParseFile()) break;
|
||||
if (matchnocase(nexttok, ".ENDC"))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Blackbox (library) mode---parse only subcircuits and models;
|
||||
// ignore all components.
|
||||
|
||||
@@ -1184,7 +1186,7 @@ skip_ends:
|
||||
else if (CountPorts(model, filenum) != 2) {
|
||||
/* Modeled device: Make sure it has the right number of ports */
|
||||
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
||||
"resistor.\n");
|
||||
"resistor.\n", model);
|
||||
goto baddevice;
|
||||
}
|
||||
usemodel = 1;
|
||||
@@ -1883,20 +1885,36 @@ char *ReadSpiceTop(char *fname, int *fnum, int blackbox)
|
||||
CurrentCell = NULL;
|
||||
|
||||
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
|
||||
char name[100];
|
||||
|
||||
SetExtension(name, fname, SPICE_EXTENSION);
|
||||
if ((filenum = OpenParseFile(name, *fnum)) < 0) {
|
||||
Fprintf(stderr,"Error in SPICE file read: No file %s\n",name);
|
||||
if (strrchr(fname, '.') == NULL) {
|
||||
char name[1024];
|
||||
SetExtension(name, fname, SPICE_EXTENSION);
|
||||
if ((filenum = OpenParseFile(name, *fnum)) < 0) {
|
||||
Fprintf(stderr, "Error in SPICE file read: No file %s\n", name);
|
||||
*fnum = filenum;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Fprintf(stderr, "Error in SPICE file read: No file %s\n", fname);
|
||||
*fnum = filenum;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure all SPICE file reading is case insensitive */
|
||||
matchfunc = matchnocase;
|
||||
matchintfunc = matchfilenocase;
|
||||
hashfunc = hashnocase;
|
||||
/* Make sure all SPICE file reading is case insensitive */
|
||||
/* BUT if a verilog file was read before it, then it will */
|
||||
/* be forced to be case sensitive, caveat end-user. */
|
||||
|
||||
if (matchfunc == match) {
|
||||
Printf("Warning: A case-sensitive file has been read and so the "
|
||||
"SPICE netlist must be treated case-sensitive to match.\n");
|
||||
}
|
||||
else {
|
||||
matchfunc = matchnocase;
|
||||
matchintfunc = matchfilenocase;
|
||||
hashfunc = hashnocase;
|
||||
}
|
||||
|
||||
InitializeHashTable(&spiceparams, OBJHASHSIZE);
|
||||
|
||||
@@ -1980,7 +1998,7 @@ void IncludeSpice(char *fname, int parent, struct cellstack **CellStackPtr,
|
||||
/* helps, if the file didn't have an extension. But */
|
||||
/* really, we're getting desperate at this point. */
|
||||
|
||||
if (strchr(fname, '.') == NULL) {
|
||||
if (strrchr(fname, '.') == NULL) {
|
||||
SetExtension(name, fname, SPICE_EXTENSION);
|
||||
filenum = OpenParseFile(name, parent);
|
||||
if (filenum < 0) {
|
||||
|
||||
+180
-21
@@ -63,6 +63,10 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
|
||||
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
|
||||
|
||||
// Used by portelement structure "flags" record.
|
||||
#define PORT_NOT_FOUND 0
|
||||
#define PORT_FOUND 1
|
||||
|
||||
// Global storage for verilog parameters
|
||||
struct hashdict verilogparams;
|
||||
// Global storage for verilog definitions
|
||||
@@ -553,9 +557,43 @@ void CleanupModule() {
|
||||
int maxnode = 0;
|
||||
int has_submodules = FALSE;
|
||||
struct objlist *sobj, *nobj, *lobj, *pobj;
|
||||
struct objlist *myLastPort, *object_it, *myNextObject;
|
||||
|
||||
if (CurrentCell == NULL) return;
|
||||
|
||||
myLastPort = NULL;
|
||||
|
||||
/* Reorder objects so that all ports come first, before nodes, because
|
||||
* parts of the code depend on it.
|
||||
*/
|
||||
|
||||
for (object_it = CurrentCell->cell; object_it && object_it->type <= 0;
|
||||
object_it = myNextObject ) {
|
||||
|
||||
myNextObject = object_it->next;
|
||||
if (!myNextObject) // end of list
|
||||
continue;
|
||||
|
||||
if (myLastPort == NULL) {
|
||||
if (object_it->type == PORT) {
|
||||
myLastPort = object_it; // port at begining of list
|
||||
myNextObject = object_it; // otherwise skips one
|
||||
}
|
||||
else if (myNextObject->type == PORT) {
|
||||
object_it->next = myNextObject->next;
|
||||
myNextObject->next = CurrentCell->cell;
|
||||
CurrentCell->cell = myNextObject;
|
||||
myLastPort = myNextObject;
|
||||
}
|
||||
}
|
||||
else if (myNextObject->type == PORT) {
|
||||
object_it->next = myNextObject->next;
|
||||
myNextObject->next = myLastPort->next;
|
||||
myLastPort->next = myNextObject;
|
||||
myLastPort = myNextObject;
|
||||
}
|
||||
}
|
||||
|
||||
for (sobj = CurrentCell->cell; sobj; sobj = sobj->next)
|
||||
if (sobj->node > maxnode)
|
||||
maxnode = sobj->node + 1;
|
||||
@@ -598,11 +636,6 @@ void CleanupModule() {
|
||||
/* Structure for stacking nested module definitions */
|
||||
/*------------------------------------------------------*/
|
||||
|
||||
struct cellstack {
|
||||
char *cellname;
|
||||
struct cellstack *next;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
extern void IncludeVerilog(char *, int, struct cellstack **, int);
|
||||
|
||||
@@ -1115,10 +1148,17 @@ skip_endmodule:
|
||||
kl->slop.dval = 0.01; // One percent default
|
||||
}
|
||||
else {
|
||||
/* Treat the parameter as a string */
|
||||
char *toks;
|
||||
|
||||
/* Treat the parameter as a string; BUT pull everything to */
|
||||
/* EOL, not just the current token. */
|
||||
toks = GetLineAtTok();
|
||||
|
||||
kl->type = PROP_STRING;
|
||||
kl->pdefault.string = strsave(nexttok);
|
||||
kl->pdefault.string = strsave(toks);
|
||||
kl->slop.dval = 0.0;
|
||||
|
||||
SkipNewLine(VLOG_DELIMITERS);
|
||||
}
|
||||
if (kl) HashPtrInstall(kl->key, kl, &verilogdefs);
|
||||
}
|
||||
@@ -1218,9 +1258,16 @@ skip_endmodule:
|
||||
lhs = LookupObject(nodename, CurrentCell);
|
||||
*aptr = '[';
|
||||
}
|
||||
else {
|
||||
strcpy(noderoot, nexttok);
|
||||
/* Set LHS to the start of the vector */
|
||||
sprintf(nodename, "%s[%d]", nexttok, wb.start);
|
||||
lhs = LookupObject(nodename, CurrentCell);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lhs = LookupObject(nexttok, CurrentCell);
|
||||
strcpy(noderoot, nexttok);
|
||||
}
|
||||
SkipTokComments(VLOG_DELIMITERS);
|
||||
if (lhs && ((!nexttok) || (!match(nexttok, "=")))) {
|
||||
@@ -1281,13 +1328,21 @@ skip_endmodule:
|
||||
if (rhs != NULL) {
|
||||
Printf("Improper assignment; left-hand side cannot "
|
||||
"be parsed.\n");
|
||||
Printf("Right-hand side is \"%s\".\n", rhs->name);
|
||||
if (j != -1)
|
||||
Printf("Right-hand side is \"%s\".\n", assignroot);
|
||||
else
|
||||
Printf("Right-hand side is \"%s\".\n", rhs->name);
|
||||
Printf("Improper expression is \"%s\".\n", nexttok);
|
||||
break;
|
||||
}
|
||||
if (lhs != NULL) {
|
||||
Printf("Improper assignment; right-hand side cannot "
|
||||
"be parsed.\n");
|
||||
Printf("Left-hand side is \"%s\".\n", lhs->name);
|
||||
if (i != -1)
|
||||
Printf("Left-hand side is \"%s\".\n", noderoot);
|
||||
else
|
||||
Printf("Left-hand side is \"%s\".\n", lhs->name);
|
||||
Printf("Improper expression is \"%s\".\n", nexttok);
|
||||
/* Not parsable, probably behavioral verilog? */
|
||||
Printf("Module '%s' is not structural verilog, "
|
||||
"making black-box.\n", model);
|
||||
@@ -1353,6 +1408,7 @@ skip_endmodule:
|
||||
char *name; // Name of port in subcell
|
||||
char *net; // Name of net connecting to port in the parent
|
||||
int width; // Width of port, if port is a bus
|
||||
char flags; // Used for marking if port was added into netlist
|
||||
struct portelement *next;
|
||||
};
|
||||
|
||||
@@ -1479,6 +1535,7 @@ nextinst:
|
||||
new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement));
|
||||
new_port->name = strsave(nexttok + 1);
|
||||
new_port->width = -1;
|
||||
new_port->flags = PORT_NOT_FOUND;
|
||||
SkipTokComments(VLOG_DELIMITERS);
|
||||
if (!match(nexttok, "(")) {
|
||||
Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok);
|
||||
@@ -1786,7 +1843,7 @@ nextinst:
|
||||
// Net is bit-sliced across array of instances.
|
||||
|
||||
if (wb.start > wb.end) {
|
||||
char *bptr, *cptr = NULL, cchar, *netname;
|
||||
char *bptr = NULL, *cptr = NULL, cchar, *netname;
|
||||
unsigned char is_bundle = 0;
|
||||
struct bus wbb;
|
||||
|
||||
@@ -1845,7 +1902,7 @@ nextinst:
|
||||
if (is_bundle &&
|
||||
((i == -1) ||
|
||||
((wbb.start > wbb.end) && (i < wbb.end)) ||
|
||||
((wbb.start < wbb.end) && (i > wbb.end)))) {
|
||||
((wbb.start <= wbb.end) && (i > wbb.end)))) {
|
||||
if (bptr) *bptr = '[';
|
||||
|
||||
netname = cptr + 1;
|
||||
@@ -1913,16 +1970,23 @@ nextinst:
|
||||
obpinidx = -1;
|
||||
while (scan != NULL) {
|
||||
if (match(obpinname, scan->name)) {
|
||||
scan->flags |= PORT_FOUND;
|
||||
break;
|
||||
}
|
||||
scan = scan->next;
|
||||
}
|
||||
if (scan == NULL) {
|
||||
Fprintf(stderr, "Error: No match in call for pin %s\n", obpinname);
|
||||
break;
|
||||
}
|
||||
char localnet[100];
|
||||
|
||||
if (GetBus(scan->net, &wb) == 0) {
|
||||
/* Assume an implicit unconnected pin */
|
||||
sprintf(localnet, "_noconnect_%d_", localcount++);
|
||||
Node(localnet);
|
||||
join(localnet, obptr->name);
|
||||
Fprintf(stderr,
|
||||
"Note: Implicit pin %s in instance %s of %s in cell %s\n",
|
||||
obpinname, locinst, modulename, CurrentCell->name);
|
||||
}
|
||||
else if (GetBus(scan->net, &wb) == 0) {
|
||||
char *bptr2;
|
||||
char *scanroot;
|
||||
scanroot = strsave(scan->net);
|
||||
@@ -1999,6 +2063,89 @@ nextinst:
|
||||
if (LookupObject(scan->net, CurrentCell) == NULL) Node(scan->net);
|
||||
join(scan->net, obptr->name);
|
||||
}
|
||||
|
||||
/* Before exiting the loop, check if all ports in the */
|
||||
/* scan list were handled. */
|
||||
|
||||
if ((obptr->next == NULL) || (obptr->next->type <= FIRSTPIN)) {
|
||||
for (scan = head; scan; scan = scan->next) {
|
||||
if (!(scan->flags & PORT_FOUND)) {
|
||||
if (tp->flags & CELL_PLACEHOLDER) {
|
||||
char tempname[128];
|
||||
int maxnode;
|
||||
|
||||
/* 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);
|
||||
ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
||||
|
||||
/* obptr->next now gets the new port. Update the */
|
||||
/* port number, and copy class and instance name. */
|
||||
nobj = GetObject();
|
||||
sprintf(tempname, "%s%s%s", obptr->instance.name,
|
||||
SEPARATOR, scan->name);
|
||||
nobj->name = strsave(tempname);
|
||||
nobj->model.class = strsave(obptr->model.class);
|
||||
nobj->instance.name = strsave(obptr->instance.name);
|
||||
nobj->type = obptr->type + 1;
|
||||
nobj->next = obptr->next;
|
||||
nobj->node = -1;
|
||||
obptr->next = nobj;
|
||||
HashPtrInstall(nobj->name, nobj, &(CurrentCell->objdict));
|
||||
|
||||
if (LookupObject(scan->net, CurrentCell) == NULL)
|
||||
Node(scan->net);
|
||||
join(scan->net, nobj->name);
|
||||
scan->flags |= PORT_FOUND;
|
||||
|
||||
/* Now any previous instance of the same cell must */
|
||||
/* insert the same additional pin as a no-connect. */
|
||||
/* NOTE: This should be running a callback on all */
|
||||
/* cells in the file, not just CurrentCell. */
|
||||
|
||||
for (sobj = CurrentCell->cell; sobj && (sobj != obptr);
|
||||
sobj = sobj->next) {
|
||||
if (sobj->type == FIRSTPIN) {
|
||||
if (match(sobj->model.class, obptr->model.class)) {
|
||||
while (sobj->next->type > FIRSTPIN)
|
||||
sobj = sobj->next;
|
||||
/* Stop when reaching the current instance */
|
||||
if (sobj->type == obptr->type + 1) break;
|
||||
nobj = GetObject();
|
||||
sprintf(tempname, "%s%s%s", sobj->instance.name,
|
||||
SEPARATOR, scan->name);
|
||||
nobj->name = strsave(tempname);
|
||||
nobj->model.class = strsave(sobj->model.class);
|
||||
nobj->instance.name = strsave(sobj->instance.name);
|
||||
nobj->type = obptr->type + 1;
|
||||
nobj->node = -1;
|
||||
nobj->next = sobj->next;
|
||||
sobj->next = nobj;
|
||||
HashPtrInstall(nobj->name, nobj,
|
||||
&(CurrentCell->objdict));
|
||||
|
||||
sprintf(tempname, "_noconnect_%d_", localcount++);
|
||||
Node(tempname);
|
||||
join(tempname, nobj->name);
|
||||
Fprintf(stderr, "Note: Implicit pin %s in instance "
|
||||
"%s of %s in cell %s\n",
|
||||
scan->name, sobj->instance.name,
|
||||
modulename, CurrentCell->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Fprintf(stderr, "Error: Instance %s has pin %s which is "
|
||||
"not in the %s cell definition.\n",
|
||||
locinst, scan->name, modulename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obptr = obptr->next;
|
||||
} while (obptr != NULL && obptr->type > FIRSTPIN);
|
||||
}
|
||||
@@ -2056,14 +2203,21 @@ char *ReadVerilogTop(char *fname, int *fnum, int blackbox)
|
||||
CurrentCell = NULL;
|
||||
|
||||
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
|
||||
char name[100];
|
||||
|
||||
SetExtension(name, fname, VERILOG_EXTENSION);
|
||||
if ((filenum = OpenParseFile(name, *fnum)) < 0) {
|
||||
Fprintf(stderr,"Error in Verilog file read: No file %s\n",name);
|
||||
*fnum = filenum;
|
||||
return NULL;
|
||||
if (strchr(fname, '.') == NULL) {
|
||||
char name[1024];
|
||||
SetExtension(name, fname, VERILOG_EXTENSION);
|
||||
if ((filenum = OpenParseFile(name, *fnum)) < 0) {
|
||||
Fprintf(stderr, "Error in Verilog file read: No file %s\n", name);
|
||||
*fnum = filenum;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Fprintf(stderr, "Error in Verilog file read: No file %s\n", fname);
|
||||
*fnum = filenum;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* All Verilog file reading is case sensitive. However: if */
|
||||
@@ -2074,6 +2228,11 @@ char *ReadVerilogTop(char *fname, int *fnum, int blackbox)
|
||||
Printf("Warning: A case-insensitive file has been read and so the "
|
||||
"verilog file must be treated case-insensitive to match.\n");
|
||||
}
|
||||
else {
|
||||
matchfunc = match;
|
||||
matchintfunc = matchfile;
|
||||
hashfunc = hash;
|
||||
}
|
||||
|
||||
InitializeHashTable(&verilogparams, OBJHASHSIZE);
|
||||
InitializeHashTable(&verilogdefs, OBJHASHSIZE);
|
||||
|
||||
@@ -4,4 +4,6 @@
|
||||
# all of its config scripts in a different directory than the configure
|
||||
# script itself.
|
||||
|
||||
( CFLAGS="-g" ; export CFLAGS ; cd scripts ; ./configure "$@" )
|
||||
export CFLAGS="$CFLAGS -g"
|
||||
cd scripts
|
||||
exec ./configure "$@"
|
||||
|
||||
@@ -8,6 +8,8 @@ SCRIPTINSTALL = $(DESTDIR)${INSTALL_PYDIR}
|
||||
|
||||
include ${NETGENDIR}/defs.mak
|
||||
|
||||
CLEANS += lvs_manager.py
|
||||
|
||||
main: lvs_manager.py
|
||||
|
||||
tcl-main: lvs_manager.py
|
||||
|
||||
@@ -1,819 +0,0 @@
|
||||
#!/bin/env python3
|
||||
#
|
||||
#--------------------------------------------------------
|
||||
# LVS Manager GUI.
|
||||
#
|
||||
# This is a Python tkinter script that handles the
|
||||
# process of running LVS and interpreting results.
|
||||
#
|
||||
#--------------------------------------------------------
|
||||
# Written by Tim Edwards
|
||||
# efabless, inc.
|
||||
# Version 1. November 30, 2016
|
||||
# Version 2. March 6, 2017. Reads JSON format output
|
||||
# Version 3. April 25, 2018. Handles layout vs. verilog
|
||||
#--------------------------------------------------------
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import signal
|
||||
import socket
|
||||
import select
|
||||
import datetime
|
||||
import contextlib
|
||||
import subprocess
|
||||
|
||||
import tkinter
|
||||
from tkinter import ttk
|
||||
from tkinter import filedialog
|
||||
|
||||
import tksimpledialog
|
||||
import tooltip
|
||||
from consoletext import ConsoleText
|
||||
from helpwindow import HelpWindow
|
||||
from treeviewsplit import TreeViewSplit
|
||||
|
||||
# User preferences file (if it exists)
|
||||
prefsfile = '~/.profile/prefs.json'
|
||||
|
||||
netgen_script_dir = '/usr/local/lib/netgen/python'
|
||||
|
||||
#------------------------------------------------------
|
||||
# Simple dialog for confirming quit
|
||||
#------------------------------------------------------
|
||||
|
||||
class ConfirmDialog(tksimpledialog.Dialog):
|
||||
def body(self, master, warning, seed):
|
||||
if warning:
|
||||
ttk.Label(master, text=warning, wraplength=500).grid(row = 0, columnspan = 2, sticky = 'wns')
|
||||
return self
|
||||
|
||||
def apply(self):
|
||||
return 'okay'
|
||||
|
||||
#------------------------------------------------------
|
||||
# Main class for this application
|
||||
#------------------------------------------------------
|
||||
|
||||
class LVSManager(ttk.Frame):
|
||||
"""LVS Manager GUI."""
|
||||
|
||||
def __init__(self, parent, *args, **kwargs):
|
||||
ttk.Frame.__init__(self, parent, *args, **kwargs)
|
||||
self.root = parent
|
||||
self.init_gui()
|
||||
parent.protocol("WM_DELETE_WINDOW", self.on_quit)
|
||||
|
||||
def on_quit(self):
|
||||
"""Exits program."""
|
||||
if self.msock:
|
||||
self.msock.close()
|
||||
quit()
|
||||
|
||||
def init_gui(self):
|
||||
"""Builds GUI."""
|
||||
global prefsfile
|
||||
|
||||
message = []
|
||||
fontsize = 11
|
||||
|
||||
# Read user preferences file, get default font size from it.
|
||||
prefspath = os.path.expanduser(prefsfile)
|
||||
if os.path.exists(prefspath):
|
||||
with open(prefspath, 'r') as f:
|
||||
self.prefs = json.load(f)
|
||||
if 'fontsize' in self.prefs:
|
||||
fontsize = self.prefs['fontsize']
|
||||
else:
|
||||
self.prefs = {}
|
||||
|
||||
s = ttk.Style()
|
||||
|
||||
available_themes = s.theme_names()
|
||||
s.theme_use(available_themes[0])
|
||||
|
||||
s.configure('bg.TFrame', background='gray40')
|
||||
s.configure('italic.TLabel', font=('Helvetica', fontsize, 'italic'))
|
||||
s.configure('title.TLabel', font=('Helvetica', fontsize, 'bold italic'),
|
||||
foreground = 'brown', anchor = 'center')
|
||||
s.configure('normal.TLabel', font=('Helvetica', fontsize))
|
||||
s.configure('red.TLabel', font=('Helvetica', fontsize), foreground = 'red')
|
||||
s.configure('green.TLabel', font=('Helvetica', fontsize), foreground = 'green3')
|
||||
s.configure('blue.TLabel', font=('Helvetica', fontsize), foreground = 'blue')
|
||||
s.configure('normal.TButton', font=('Helvetica', fontsize),
|
||||
border = 3, relief = 'raised')
|
||||
s.configure('red.TButton', font=('Helvetica', fontsize), foreground = 'red',
|
||||
border = 3, relief = 'raised')
|
||||
s.configure('green.TButton', font=('Helvetica', fontsize), foreground = 'green3',
|
||||
border = 3, relief = 'raised')
|
||||
s.configure('blue.TButton', font=('Helvetica', fontsize), foreground = 'blue',
|
||||
border = 3, relief = 'raised')
|
||||
s.configure('redtitle.TButton', font=('Helvetica', fontsize, 'bold italic'),
|
||||
foreground = 'red', border = 3, relief = 'raised')
|
||||
s.configure('bluetitle.TButton', font=('Helvetica', fontsize, 'bold italic'),
|
||||
foreground = 'blue', border = 3, relief = 'raised')
|
||||
|
||||
# These values to be overridden from arguments
|
||||
self.rootpath = None
|
||||
self.project = None
|
||||
self.logfile = None
|
||||
self.msock = None
|
||||
self.help = None
|
||||
|
||||
# Create the help window
|
||||
if os.path.exists(netgen_script_dir + '/netgen_help.txt'):
|
||||
self.help = HelpWindow(self, fontsize = fontsize)
|
||||
with io.StringIO() as buf, contextlib.redirect_stdout(buf):
|
||||
self.help.add_pages_from_file('lvs_help.txt')
|
||||
message = buf.getvalue()
|
||||
|
||||
# Set the help display to the first page
|
||||
self.help.page(0)
|
||||
|
||||
# Variables used by option menus and other stuff
|
||||
self.project = "(no selection)"
|
||||
self.layout = "(default)"
|
||||
self.schematic = "(default)"
|
||||
self.tech = "(none)"
|
||||
self.lvs_setup = ''
|
||||
self.lvsdata = {}
|
||||
|
||||
# Root window title
|
||||
self.root.title('LVS Manager')
|
||||
self.root.option_add('*tearOff', 'FALSE')
|
||||
self.pack(side = 'top', fill = 'both', expand = 'true')
|
||||
|
||||
pane = tkinter.PanedWindow(self, orient = 'vertical', sashrelief='groove', sashwidth=6)
|
||||
pane.pack(side = 'top', fill = 'both', expand = 'true')
|
||||
self.toppane = ttk.Frame(pane)
|
||||
self.botpane = ttk.Frame(pane)
|
||||
|
||||
# Get username
|
||||
if 'username' in self.prefs:
|
||||
username = self.prefs['username']
|
||||
else:
|
||||
username = os.environ['USER']
|
||||
|
||||
# Label with the user
|
||||
self.toppane.title_frame = ttk.Frame(self.toppane)
|
||||
self.toppane.title_frame.pack(side = 'top', fill = 'x')
|
||||
|
||||
self.toppane.title_frame.title = ttk.Label(self.toppane.title_frame, text='User:', style = 'red.TLabel')
|
||||
self.toppane.title_frame.user = ttk.Label(self.toppane.title_frame, text=username, style = 'blue.TLabel')
|
||||
|
||||
self.toppane.title_frame.title.grid(column=0, row=0, ipadx = 5)
|
||||
self.toppane.title_frame.user.grid(column=1, row=0, ipadx = 5)
|
||||
|
||||
self.toppane.title2_frame = ttk.Frame(self.toppane)
|
||||
self.toppane.title2_frame.pack(side = 'top', fill = 'x')
|
||||
self.toppane.title2_frame.project_label = ttk.Label(self.toppane.title2_frame, text="Project:",
|
||||
style = 'title.TLabel')
|
||||
self.toppane.title2_frame.project_label.grid(column=0, row=0, ipadx = 5)
|
||||
|
||||
# New project select button
|
||||
self.toppane.title2_frame.project_select = ttk.Button(self.toppane.title2_frame,
|
||||
text=self.project, style='normal.TButton', command=self.choose_project)
|
||||
self.toppane.title2_frame.project_select.grid(column=1, row=0, ipadx = 5)
|
||||
|
||||
tooltip.ToolTip(self.toppane.title2_frame.project_select,
|
||||
text = "Select new project")
|
||||
|
||||
# Show path to project
|
||||
self.toppane.title2_frame.path_label = ttk.Label(self.toppane.title2_frame, text=self.project,
|
||||
style = 'normal.TLabel')
|
||||
self.toppane.title2_frame.path_label.grid(column=2, row=0, ipadx = 5, padx = 10)
|
||||
|
||||
# Show top-level layout cellname with select button. Initial cell name is the top-level cell.
|
||||
self.toppane.title2_frame.tech_label = ttk.Label(self.toppane.title2_frame, text="Technology setup:",
|
||||
style = 'title.TLabel')
|
||||
|
||||
self.toppane.title2_frame.tech_label.grid(column=3, row=0, ipadx = 5)
|
||||
self.toppane.title2_frame.tech_select = ttk.Button(self.toppane.title2_frame,
|
||||
text=self.tech, style='normal.TButton', command=self.choose_tech)
|
||||
self.toppane.title2_frame.tech_select.grid(column=4, row=0, ipadx = 3, padx = 3)
|
||||
|
||||
self.toppane.title2_frame.layout_label = ttk.Label(self.toppane.title2_frame, text="Layout:",
|
||||
style = 'title.TLabel')
|
||||
self.toppane.title2_frame.layout_label.grid(column=0, row=1, ipadx = 5)
|
||||
self.toppane.title2_frame.layout_select = ttk.Button(self.toppane.title2_frame,
|
||||
text=self.layout, style='normal.TButton', command=self.choose_layout)
|
||||
self.toppane.title2_frame.layout_select.grid(column=1, row=1, ipadx = 3, padx = 3)
|
||||
|
||||
# Show top-level schematic cellname with select button. Initial cell name is the top-level cell.
|
||||
self.toppane.title2_frame.schem_label = ttk.Label(self.toppane.title2_frame, text="Schematic:",
|
||||
style = 'title.TLabel')
|
||||
self.toppane.title2_frame.schem_label.grid(column=3, row=1, ipadx = 5)
|
||||
self.toppane.title2_frame.schem_select = ttk.Button(self.toppane.title2_frame,
|
||||
text=self.schematic, style='normal.TButton', command=self.choose_netlist)
|
||||
self.toppane.title2_frame.schem_select.grid(column=4, row=1, ipadx = 3, padx = 3)
|
||||
|
||||
tooltip.ToolTip(self.toppane.title2_frame.project_select,
|
||||
text = "Select new project")
|
||||
tooltip.ToolTip(self.toppane.title2_frame.layout_select,
|
||||
text = "Select a layout subcirucit to compare")
|
||||
tooltip.ToolTip(self.toppane.title2_frame.schem_select,
|
||||
text = "Select a schematic subcirucit to compare")
|
||||
|
||||
#---------------------------------------------
|
||||
ttk.Separator(self.toppane, orient='horizontal').pack(side = 'top', fill = 'x')
|
||||
#---------------------------------------------
|
||||
|
||||
# Create listbox of Circuit1 vs. Circuit2 results
|
||||
height = 10
|
||||
self.toppane.lvsreport = TreeViewSplit(self.toppane, fontsize = fontsize)
|
||||
self.toppane.lvsreport.populate("Layout:", [], "Schematic:", [],
|
||||
[["Run", True, self.run_lvs],
|
||||
# ["Find", True, self.findrecord]
|
||||
], height = height)
|
||||
self.toppane.lvsreport.set_title("Line")
|
||||
self.toppane.lvsreport.pack(side = 'top', fill = 'both', expand = 'true')
|
||||
|
||||
tooltip.ToolTip(self.toppane.lvsreport.get_button(0), text="Run LVS")
|
||||
|
||||
#---------------------------------------------
|
||||
# ttk.Separator(self, orient='horizontal').grid(column=0, row=3, sticky='ew')
|
||||
#---------------------------------------------
|
||||
|
||||
# Add a text window below the project name to capture output. Redirect
|
||||
# print statements to it.
|
||||
|
||||
self.botpane.console = ttk.Frame(self.botpane)
|
||||
self.botpane.console.pack(side = 'top', fill = 'both', expand = 'true')
|
||||
|
||||
self.text_box = ConsoleText(self.botpane.console, wrap='word', height = 4)
|
||||
self.text_box.pack(side='left', fill='both', expand='true')
|
||||
console_scrollbar = ttk.Scrollbar(self.botpane.console)
|
||||
console_scrollbar.pack(side='right', fill='y')
|
||||
# attach console to scrollbar
|
||||
self.text_box.config(yscrollcommand = console_scrollbar.set)
|
||||
console_scrollbar.config(command = self.text_box.yview)
|
||||
|
||||
# Add button bar at the bottom of the window
|
||||
self.botpane.bbar = ttk.Frame(self.botpane)
|
||||
self.botpane.bbar.pack(side = 'top', fill = 'x')
|
||||
|
||||
# Define the "quit" button and action
|
||||
self.botpane.bbar.quit_button = ttk.Button(self.botpane.bbar, text='Quit', command=self.on_quit,
|
||||
style = 'normal.TButton')
|
||||
self.botpane.bbar.quit_button.grid(column=0, row=0, padx = 5)
|
||||
|
||||
# Define help button
|
||||
if self.help:
|
||||
self.botpane.bbar.help_button = ttk.Button(self.botpane.bbar, text='Help',
|
||||
command=self.help.open, style = 'normal.TButton')
|
||||
self.botpane.bbar.help_button.grid(column = 2, row = 0, padx = 5)
|
||||
tooltip.ToolTip(self.botpane.bbar.help_button, text = "Show help window")
|
||||
|
||||
# Add the panes once the internal geometry is known.
|
||||
pane.add(self.toppane)
|
||||
pane.add(self.botpane)
|
||||
pane.paneconfig(self.toppane, stretch='first')
|
||||
|
||||
# Redirect stdout and stderr to the console as the last thing to do. . .
|
||||
# Otherwise errors in the GUI get sucked into the void.
|
||||
|
||||
self.stdout = sys.stdout
|
||||
self.stderr = sys.stderr
|
||||
sys.stdout = ConsoleText.StdoutRedirector(self.text_box)
|
||||
sys.stderr = ConsoleText.StderrRedirector(self.text_box)
|
||||
|
||||
if message:
|
||||
print(message)
|
||||
|
||||
def logprint(self, message, doflush=False):
|
||||
if self.logfile:
|
||||
self.logfile.buffer.write(message.encode('utf-8'))
|
||||
self.logfile.buffer.write('\n'.encode('utf-8'))
|
||||
if doflush:
|
||||
self.logfile.flush()
|
||||
|
||||
def printout(self, output):
|
||||
# Generate output
|
||||
if not output:
|
||||
return
|
||||
|
||||
outlines = output.splitlines()
|
||||
for line in outlines:
|
||||
try:
|
||||
print(line)
|
||||
except TypeError:
|
||||
line = line.decode('utf-8')
|
||||
pritn(line)
|
||||
|
||||
def printwarn(self, output):
|
||||
# Check output for warning or error
|
||||
if not output:
|
||||
return 0
|
||||
|
||||
warnrex = re.compile('.*warning', re.IGNORECASE)
|
||||
errrex = re.compile('.*error', re.IGNORECASE)
|
||||
|
||||
errors = 0
|
||||
outlines = output.splitlines()
|
||||
for line in outlines:
|
||||
try:
|
||||
wmatch = warnrex.match(line)
|
||||
except TypeError:
|
||||
line = line.decode('utf-8')
|
||||
wmatch = warnrex.match(line)
|
||||
ematch = errrex.match(line)
|
||||
if ematch:
|
||||
errors += 1
|
||||
if ematch or wmatch:
|
||||
print(line)
|
||||
return errors
|
||||
|
||||
def choose_tech(self):
|
||||
try:
|
||||
project_path = self.rootpath
|
||||
initdirname = self.rootpath + '/tech',
|
||||
except:
|
||||
print('Must choose a project first.')
|
||||
return
|
||||
techname = filedialog.askopenfilename(multiple=False,
|
||||
initialdir = initdirname,
|
||||
filetypes = (("Tcl script", "*.tcl"),("All Files","*.*")),
|
||||
title = "Choose a netgen technology setup script.")
|
||||
if techname != '':
|
||||
print("Selected technology setup script " + techname)
|
||||
techbase = os.path.split(techname)[1]
|
||||
self.tech = os.path.splitext(techbase)[0]
|
||||
self.lvs_setup = techname
|
||||
self.toppane.title2_frame.layout_select.config(text = self.tech)
|
||||
|
||||
def choose_layout(self):
|
||||
try:
|
||||
project_path = self.rootpath
|
||||
initdirname = self.rootpath + '/layout',
|
||||
except:
|
||||
print('Must choose a project first.')
|
||||
return
|
||||
cellname = filedialog.askopenfilename(multiple=False,
|
||||
initialdir = initdirname,
|
||||
filetypes = (("Magic layout", "*.mag"),("All Files","*.*")),
|
||||
title = "Choose a layout cell to compare.")
|
||||
if cellname != '':
|
||||
print("Selected compare cell " + cellname)
|
||||
cellbase = os.path.split(cellname)[1]
|
||||
self.layout = os.path.splitext(cellbase)[0]
|
||||
self.toppane.title2_frame.layout_select.config(text = self.layout)
|
||||
|
||||
def choose_netlist(self):
|
||||
try:
|
||||
project_path = self.rootpath
|
||||
initdirname = self.rootpath + '/netlist/' + self.project + '.spi'
|
||||
except:
|
||||
print('Must choose a project first.')
|
||||
return
|
||||
cellname = filedialog.askopenfilename(multiple=False,
|
||||
initialdir = initdirname,
|
||||
filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")),
|
||||
title = "Choose a netlist to compare.")
|
||||
if cellname != '':
|
||||
print("Selected compare cell " + cellname)
|
||||
cellbase = os.path.split(cellname)[1]
|
||||
self.schematic = os.path.splitext(cellbase)[0]
|
||||
self.toppane.title2_frame.schem_select.config(text = self.schematic)
|
||||
fileext = os.path.splitext(cellbase)[1]
|
||||
if fileext == '.v':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
|
||||
elif fileext == '.sp' or fileext == '.spi' or fileext == '.spice' or fileext == '.spc' or fileext == '.ckt':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:')
|
||||
elif fileext == '.cdl':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:')
|
||||
else:
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Unknown netlist:')
|
||||
|
||||
def choose_project(self):
|
||||
project = filedialog.askdirectory(initialdir = os.getcwd(),
|
||||
title = "Find a project.")
|
||||
if project != '':
|
||||
print("Selected project " + str(project))
|
||||
result = self.set_project(project)
|
||||
|
||||
def set_project(self, rootpath, project_name=None):
|
||||
|
||||
# Check if rootpath is valid. For LVS, there should be subdirectories
|
||||
# "layout/" and "netlist/" or "verilog/".
|
||||
|
||||
haslay = os.path.isdir(rootpath + '/layout')
|
||||
hasvlog = os.path.isdir(rootpath + '/verilog')
|
||||
hasnet = os.path.isdir(rootpath + '/netlist')
|
||||
if not haslay or not (hasvlog or hasnet):
|
||||
if not haslay:
|
||||
print("Project path has no layout (/layout) subdirectory.")
|
||||
if not (hasvlog or hasnet):
|
||||
print("Project path has no verilog (/verilog), or netlist (/netlist) subdirectory.")
|
||||
# Continue anyway; assume that netlists will be selected manually
|
||||
|
||||
if self.logfile:
|
||||
self.logfile.close()
|
||||
self.logfile = None
|
||||
|
||||
if not project_name:
|
||||
project = os.path.split(rootpath)[1]
|
||||
else:
|
||||
project = project_name
|
||||
|
||||
if self.project != project:
|
||||
|
||||
self.rootpath = rootpath
|
||||
self.project = project
|
||||
|
||||
# Clear out old project data
|
||||
self.toppane.lvsreport.repopulate([], [])
|
||||
|
||||
# Close any open logfile.
|
||||
if self.logfile:
|
||||
self.logfile.close()
|
||||
self.logfile = None
|
||||
|
||||
# Put new log file called 'lvs.log' in the mag/ subdirectory
|
||||
self.logfile = open(rootpath + '/layout/lvs.log', 'w')
|
||||
# Print some initial information to the logfile.
|
||||
self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'),
|
||||
doflush=True)
|
||||
|
||||
# Update project button
|
||||
self.toppane.title2_frame.project_select.config(text = self.project)
|
||||
self.toppane.title2_frame.path_label.config(text = self.rootpath)
|
||||
# Cell name is the same as project name initially
|
||||
self.layout = self.project
|
||||
self.schematic = self.project
|
||||
self.toppane.title2_frame.layout_select.config(text = self.layout)
|
||||
self.toppane.title2_frame.schem_select.config(text = self.schematic)
|
||||
|
||||
# Update schematic button
|
||||
if os.path.isfile(rootpath + '/verilog/' + self.schematic + '.v'):
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
|
||||
else:
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:')
|
||||
|
||||
# If there is a comparison file that post-dates both netlists, load it.
|
||||
self.check_lvs()
|
||||
return True
|
||||
|
||||
def check_layout_out_of_date(self, spipath, layoutpath):
|
||||
# Check if a netlist (spipath) is out-of-date relative to the layouts
|
||||
# (layoutpath). Need to read the netlist and check all of the subcells.
|
||||
need_capture = False
|
||||
if not os.path.isfile(spipath):
|
||||
return True
|
||||
if os.path.isfile(layoutpath):
|
||||
spi_statbuf = os.stat(spipath)
|
||||
lay_statbuf = os.stat(layoutpath)
|
||||
if spi_statbuf.st_mtime < lay_statbuf.st_mtime:
|
||||
# netlist exists but is out-of-date
|
||||
need_capture = True
|
||||
else:
|
||||
# only found that the top-level-layout is older than the
|
||||
# netlist. Now need to read the netlist, find all subcircuits,
|
||||
# and check those dates, too.
|
||||
layoutdir = os.path.split(layoutpath)[0]
|
||||
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
|
||||
with open(spipath, 'r') as ifile:
|
||||
duttext = ifile.read()
|
||||
|
||||
dutlines = duttext.replace('\n+', ' ').splitlines()
|
||||
for line in dutlines:
|
||||
lmatch = subrex.match(line)
|
||||
if lmatch:
|
||||
subname = lmatch.group(1)
|
||||
sublayout = layoutdir + '/' + subname + '.mag'
|
||||
# subcircuits that cannot be found in the current directory are
|
||||
# assumed to be library components and therefore never out-of-date.
|
||||
if os.path.exists(sublayout):
|
||||
sub_statbuf = os.stat(sublayout)
|
||||
if spi_statbuf.st_mtime < lay_statbuf.st_mtime:
|
||||
# netlist exists but is out-of-date
|
||||
need_capture = True
|
||||
break
|
||||
return need_capture
|
||||
|
||||
def check_schematic_out_of_date(self, spipath, schempath):
|
||||
# Check if a netlist (spipath) is out-of-date relative to the schematics
|
||||
# (schempath). Need to read the netlist and check all of the subcells.
|
||||
need_capture = False
|
||||
if not os.path.isfile(spipath):
|
||||
return True
|
||||
if os.path.isfile(schempath):
|
||||
spi_statbuf = os.stat(spipath)
|
||||
sch_statbuf = os.stat(schempath)
|
||||
if spi_statbuf.st_mtime < sch_statbuf.st_mtime:
|
||||
# netlist exists but is out-of-date
|
||||
need_capture = True
|
||||
else:
|
||||
# only found that the top-level-schematic is older than the
|
||||
# netlist. Now need to read the netlist, find all subcircuits,
|
||||
# and check those dates, too.
|
||||
schemdir = os.path.split(schempath)[0]
|
||||
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
|
||||
with open(spipath, 'r') as ifile:
|
||||
duttext = ifile.read()
|
||||
|
||||
dutlines = duttext.replace('\n+', ' ').splitlines()
|
||||
for line in dutlines:
|
||||
lmatch = subrex.match(line)
|
||||
if lmatch:
|
||||
subname = lmatch.group(1)
|
||||
# NOTE: Electric uses library:cell internally to track libraries,
|
||||
# and maps the ":" to "__" in the netlist. Not entirely certain that
|
||||
# the double-underscore uniquely identifies the library:cell. . .
|
||||
librex = re.compile('(.*)__(.*)', re.IGNORECASE)
|
||||
lmatch = librex.match(subname)
|
||||
if lmatch:
|
||||
elecpath = os.path.split(os.path.split(schempath)[0])[0]
|
||||
libname = lmatch.group(1)
|
||||
subschem = elecpath + '/' + libname + '.delib/' + lmatch.group(2) + '.sch'
|
||||
else:
|
||||
libname = {}
|
||||
subschem = schemdir + '/' + subname + '.sch'
|
||||
# subcircuits that cannot be found in the current directory are
|
||||
# assumed to be library components and therefore never out-of-date.
|
||||
if os.path.exists(subschem):
|
||||
sub_statbuf = os.stat(subschem)
|
||||
if spi_statbuf.st_mtime < sub_statbuf.st_mtime:
|
||||
# netlist exists but is out-of-date
|
||||
need_capture = True
|
||||
break
|
||||
# mapping of characters to what's allowed in SPICE makes finding
|
||||
# the associated schematic file a bit difficult. Requires wild-card
|
||||
# searching.
|
||||
elif libname:
|
||||
restr = lmatch.group(2) + '.sch'
|
||||
restr = restr.replace('.', '\.')
|
||||
restr = restr.replace('_', '.')
|
||||
schrex = re.compile(restr, re.IGNORECASE)
|
||||
libpath = elecpath + '/' + libname + '.delib'
|
||||
if os.path.exists(libpath):
|
||||
liblist = os.listdir(libpath)
|
||||
for file in liblist:
|
||||
lmatch = schrex.match(file)
|
||||
if lmatch:
|
||||
subschem = libpath + '/' + file
|
||||
sub_statbuf = os.stat(subschem)
|
||||
if spi_statbuf.st_mtime < sch_statbuf.st_mtime:
|
||||
# netlist exists but is out-of-date
|
||||
need_capture = True
|
||||
break
|
||||
return need_capture
|
||||
|
||||
def check_lvs(self):
|
||||
# If both netlists exist, and comp.json is more recent than both, then
|
||||
# load LVS results from comp.json
|
||||
project_path = self.rootpath
|
||||
project_name = self.project
|
||||
layout_path = project_path + '/layout/' + project_name + '.spc'
|
||||
net_path = project_path + '/netlist/' + project_name + '.spi'
|
||||
comp_path = project_path + '/layout/comp.json'
|
||||
|
||||
if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path):
|
||||
magtime = os.stat(layout_path).st_mtime
|
||||
schemtime = os.stat(net_path).st_mtime
|
||||
comptime = os.stat(comp_path).st_mtime
|
||||
if comptime > magtime and comptime > schemtime:
|
||||
print("Loading LVS results from file.")
|
||||
self.generate(comp_path)
|
||||
|
||||
def generate_layout_netlist(self, layout_path, layout_src, project_path):
|
||||
# Does layout netlist exist and is it current?
|
||||
if self.check_layout_out_of_date(layout_path, layout_src):
|
||||
print('Generating layout netlist.')
|
||||
self.update_idletasks()
|
||||
mproc = subprocess.Popen(['magic', '-dnull', '-noconsole',
|
||||
self.layout], stdin = subprocess.PIPE, stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE, cwd = project_path + '/layout',
|
||||
universal_newlines = True)
|
||||
mproc.stdin.write("select top cell\n")
|
||||
mproc.stdin.write("expand\n")
|
||||
mproc.stdin.write("extract all\n")
|
||||
mproc.stdin.write("ext2spice hierarchy on\n")
|
||||
mproc.stdin.write("ext2spice format ngspice\n")
|
||||
mproc.stdin.write("ext2spice scale off\n")
|
||||
mproc.stdin.write("ext2spice renumber off\n")
|
||||
mproc.stdin.write("ext2spice subcircuit top auto\n")
|
||||
mproc.stdin.write("ext2spice cthresh infinite\n")
|
||||
mproc.stdin.write("ext2spice rthresh infinite\n")
|
||||
mproc.stdin.write("ext2spice blackbox on\n")
|
||||
mproc.stdin.write("ext2spice -o " + self.layout + ".spi\n")
|
||||
mproc.stdin.write("quit -noprompt\n")
|
||||
magicout = mproc.communicate()[0]
|
||||
self.printwarn(magicout)
|
||||
if mproc.returncode != 0:
|
||||
print('Failure to generate new layout netlist.')
|
||||
return False
|
||||
|
||||
# Move .spi netlist to project_dir/netlist/lvs/
|
||||
shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path)
|
||||
# Remove extraction files
|
||||
for file in os.listdir(project_path + '/layout'):
|
||||
if os.path.splitext(file)[1] == '.ext':
|
||||
os.remove(project_path + '/layout/' + file)
|
||||
else:
|
||||
print('Layout netlist is up-to-date, not regenerating.')
|
||||
return True
|
||||
|
||||
def run_lvs(self, value):
|
||||
# "value" is ignored (?)
|
||||
|
||||
# Check if netlists exist and are current; otherwise create them.
|
||||
# Then run LVS.
|
||||
|
||||
project_path = self.rootpath
|
||||
project_name = self.project
|
||||
comp_path = project_path + '/layout/comp.json'
|
||||
has_vlog = False
|
||||
vlog_path = project_path + '/verilog/' + project_name + '.v'
|
||||
|
||||
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi'
|
||||
net_path = project_path + '/netlist/schem/' + self.schematic + '.spi'
|
||||
layout_src = project_path + '/layout/' + self.layout + '.mag'
|
||||
|
||||
# Does the setup file exist (this is optional)?
|
||||
if self.lvs_setup != '' and not os.path.isfile('setup.tcl'):
|
||||
print('No technology setup file selected.')
|
||||
|
||||
# Does schematic netlist exist?
|
||||
if not os.path.isfile(vlog_path) and not os.path.isfile(net_path):
|
||||
print('Error: No schematic netlist or verilog netlist.')
|
||||
return
|
||||
|
||||
# Does LVS netlist subdirectory exist?
|
||||
if not os.path.exists(project_path + '/netlist/lvs'):
|
||||
os.makedirs(project_path + '/netlist/lvs')
|
||||
|
||||
# Does layout netlist exist and is it current?
|
||||
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
|
||||
return False
|
||||
|
||||
# Final check
|
||||
if not os.path.isfile(layout_path):
|
||||
print('Error: No netlist generated from magic.')
|
||||
return
|
||||
|
||||
else:
|
||||
# Read in netlist and convert commas from [X,Y] arrays to vertical bars
|
||||
# as something that can be converted back as necessary. ngspice treats
|
||||
# commas as special characters for some reason.
|
||||
with open(layout_path) as ifile:
|
||||
spitext = ifile.read()
|
||||
|
||||
# spilines = spitext.replace(',', '|')
|
||||
# with open(layout_path, 'w') as ofile:
|
||||
# ofile.write(spilines)
|
||||
|
||||
# Check the netlist to see if the cell to match is a subcircuit. If
|
||||
# not, then assume it is the top level.
|
||||
|
||||
is_subckt = False
|
||||
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
|
||||
dutlines = spitext.replace('\n+', ' ').splitlines()
|
||||
for line in dutlines:
|
||||
lmatch = subrex.match(line)
|
||||
if lmatch:
|
||||
subname = lmatch.group(1)
|
||||
if subname == self.layout:
|
||||
is_subckt = True
|
||||
break
|
||||
|
||||
if is_subckt:
|
||||
layout_arg = layout_path + ' ' + self.layout
|
||||
layout_text = '"' + layout_arg + '"'
|
||||
else:
|
||||
layout_arg = layout_path
|
||||
layout_text = layout_arg
|
||||
|
||||
if has_vlog:
|
||||
schem_arg = vlog_path + ' ' + self.schematic
|
||||
else:
|
||||
schem_arg = net_path + ' ' + self.schematic
|
||||
schem_text = '"' + schem_arg + '"'
|
||||
|
||||
# Remove any previous comparison output file
|
||||
comp_out_path = os.path.splitext(comp_path)[0] + '.out'
|
||||
if os.path.exists(comp_out_path):
|
||||
os.remove(comp_out_path)
|
||||
|
||||
# Run netgen as subprocess
|
||||
print('Running: netgen -batch lvs ' + layout_text +
|
||||
' ' + schem_text + ' ' + self.lvs_setup + ' ' + comp_out_path +
|
||||
' -json -blackbox')
|
||||
# Note: Because arguments to subprocess are list items, the {filename cell}
|
||||
# pair does *not* have to be quoted or braced. Doing so causes a parse
|
||||
# error.
|
||||
self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs',
|
||||
layout_arg, schem_arg,
|
||||
self.lvs_setup, comp_out_path, '-json', '-blackbox'],
|
||||
cwd=project_path + '/layout',
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
|
||||
# This is largely unnecessary as netgen usually runs to completion very quickly.
|
||||
self.watchclock(comp_path)
|
||||
|
||||
def watchclock(self, filename):
|
||||
if self.lvsproc == None:
|
||||
return
|
||||
|
||||
lvs_status = self.lvsproc.poll()
|
||||
sresult = select.select([self.lvsproc.stdout, self.lvsproc.stderr], [], [], 0)[0]
|
||||
if self.lvsproc.stdout in sresult:
|
||||
outstring = self.lvsproc.stdout.readline().decode().strip()
|
||||
self.logprint(outstring, doflush=True)
|
||||
print(outstring)
|
||||
elif self.lvsproc.stderr in sresult:
|
||||
errstring = self.lvsproc.stderr.readline().decode().strip()
|
||||
self.logprint(errstring, doflush = True)
|
||||
print(errstring, file=sys.stderr)
|
||||
|
||||
if lvs_status != None:
|
||||
print("netgen LVS exited with status " + str(lvs_status))
|
||||
self.lvsproc = None
|
||||
if lvs_status != 0:
|
||||
print('Errors encountered in LVS.')
|
||||
self.logprint('Errors in LVS, lvs status = ' + str(lvs_status), doflush=True)
|
||||
# Done; now read comp.json and fill the treeview listbox.
|
||||
self.generate(filename)
|
||||
else:
|
||||
self.after(500, lambda: self.watchclock(filename))
|
||||
|
||||
# Generate display from "comp.out" file (json file now preferred)
|
||||
|
||||
def generate_orig(self, lvspath):
|
||||
lefttext = []
|
||||
righttext = []
|
||||
print("Reading LVS output file " + lvspath)
|
||||
if os.path.exists(lvspath):
|
||||
with open(lvspath, 'r') as ifile:
|
||||
lvslines = ifile.read().splitlines()
|
||||
for line in lvslines:
|
||||
if '|' in line:
|
||||
# parts = line.split('|')
|
||||
# lefttext.append(parts[0])
|
||||
# righttext.append(parts[1])
|
||||
lefttext.append(line[0:42].strip())
|
||||
righttext.append(line[44:].strip())
|
||||
else:
|
||||
lefttext.append(line)
|
||||
righttext.append('')
|
||||
# Populate treeview with text
|
||||
self.toppane.lvsreport.repopulate(lefttext, righttext)
|
||||
|
||||
else:
|
||||
print("Error: No output file generated from LVS.")
|
||||
|
||||
# Generate output from LVS report JSON file comp.json
|
||||
|
||||
def generate(self, lvspath):
|
||||
lefttext = []
|
||||
righttext = []
|
||||
print("Reading LVS output file " + lvspath)
|
||||
if os.path.exists(lvspath):
|
||||
with open(lvspath, 'r') as ifile:
|
||||
self.lvsdata = json.load(ifile)
|
||||
|
||||
# Populate treeview with text
|
||||
self.toppane.lvsreport.json_repopulate(self.lvsdata)
|
||||
|
||||
else:
|
||||
print("Error: No output file generated from LVS.")
|
||||
|
||||
def findrecord(self, value):
|
||||
print("Unimplemented function")
|
||||
|
||||
def findrecord_test(self, value):
|
||||
# Check if socket is defined; if not, attempt to open one
|
||||
if not self.msock:
|
||||
try:
|
||||
self.msock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
except:
|
||||
print("No response from layout tool.")
|
||||
|
||||
if self.msock:
|
||||
self.msock.connect(("0.0.0.0", 12946))
|
||||
self.msock.setblocking(False)
|
||||
if self.msock:
|
||||
# Pull name of net or device from 'value'
|
||||
# This is a test:
|
||||
self.msock.sendall(b'box 0 0 100 100\r\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
options = []
|
||||
arguments = []
|
||||
for item in sys.argv[1:]:
|
||||
if item.find('-', 0) == 0:
|
||||
options.append(item)
|
||||
else:
|
||||
arguments.append(item)
|
||||
|
||||
root = tkinter.Tk()
|
||||
app = LVSManager(root)
|
||||
if arguments:
|
||||
if len(arguments) >= 2:
|
||||
app.set_project(arguments[0], project_name=arguments[1])
|
||||
else:
|
||||
app.set_project(arguments[0])
|
||||
|
||||
root.mainloop()
|
||||
+108
-36
@@ -343,7 +343,7 @@ class LVSManager(ttk.Frame):
|
||||
techbase = os.path.split(techname)[1]
|
||||
self.tech = os.path.splitext(techbase)[0]
|
||||
self.lvs_setup = techname
|
||||
self.toppane.title2_frame.layout_select.config(text = self.tech)
|
||||
self.toppane.title2_frame.tech_select.config(text = self.tech)
|
||||
|
||||
def choose_layout(self):
|
||||
try:
|
||||
@@ -358,30 +358,37 @@ class LVSManager(ttk.Frame):
|
||||
title = "Choose a layout cell to compare.")
|
||||
if cellname != '':
|
||||
print("Selected compare cell " + cellname)
|
||||
self.layout = cellname
|
||||
cellbase = os.path.split(cellname)[1]
|
||||
self.layout = os.path.splitext(cellbase)[0]
|
||||
self.toppane.title2_frame.layout_select.config(text = self.layout)
|
||||
layoutname = os.path.splitext(cellbase)[0]
|
||||
self.toppane.title2_frame.layout_select.config(text = layoutname)
|
||||
fileext = os.path.splitext(cellbase)[1]
|
||||
if fileext == '.mag':
|
||||
self.toppane.title2_frame.layout_label.config(text = 'Layout:')
|
||||
else:
|
||||
self.toppane.title2_frame.layout_label.config(text = 'Layout netlist:')
|
||||
|
||||
def choose_netlist(self):
|
||||
try:
|
||||
project_path = self.rootpath
|
||||
initdirname = self.rootpath + '/netlist/' + self.project + '.spi'
|
||||
initdirname = self.rootpath + '/netlist/' + self.project + '.spice'
|
||||
except:
|
||||
print('Must choose a project first.')
|
||||
return
|
||||
cellname = filedialog.askopenfilename(multiple=False,
|
||||
initialdir = initdirname,
|
||||
filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")),
|
||||
filetypes = (("Spice netlist", "*.spice"),("Verilog netlist", "*.v"),("All Files","*.*")),
|
||||
title = "Choose a netlist to compare.")
|
||||
if cellname != '':
|
||||
print("Selected compare cell " + cellname)
|
||||
self.schematic = cellname
|
||||
cellbase = os.path.split(cellname)[1]
|
||||
self.schematic = os.path.splitext(cellbase)[0]
|
||||
self.toppane.title2_frame.schem_select.config(text = self.schematic)
|
||||
schematic_name = os.path.splitext(cellbase)[0]
|
||||
self.toppane.title2_frame.schem_select.config(text = schematic_name)
|
||||
fileext = os.path.splitext(cellbase)[1]
|
||||
if fileext == '.v':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
|
||||
elif fileext == '.sp' or fileext == '.spi' or fileext == '.spice' or fileext == '.spc' or fileext == '.ckt':
|
||||
elif fileext == '.sp' or fileext == '.spice' or fileext == '.spi' or fileext == '.spc' or fileext == '.ckt':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:')
|
||||
elif fileext == '.cdl':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:')
|
||||
@@ -433,7 +440,10 @@ class LVSManager(ttk.Frame):
|
||||
self.logfile = None
|
||||
|
||||
# Put new log file called 'lvs.log' in the mag/ subdirectory
|
||||
self.logfile = open(rootpath + '/layout/lvs.log', 'w')
|
||||
if os.path.exists(rootpath + '/layout'):
|
||||
self.logfile = open(rootpath + '/layout/lvs.log', 'w')
|
||||
else:
|
||||
self.logfile = open(rootpath + '/lvs.log', 'w')
|
||||
# Print some initial information to the logfile.
|
||||
self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'),
|
||||
doflush=True)
|
||||
@@ -444,15 +454,23 @@ class LVSManager(ttk.Frame):
|
||||
# Cell name is the same as project name initially
|
||||
self.layout = self.project
|
||||
self.schematic = self.project
|
||||
self.toppane.title2_frame.layout_select.config(text = self.layout)
|
||||
self.toppane.title2_frame.schem_select.config(text = self.schematic)
|
||||
layname = os.path.splitext(os.path.split(self.layout)[1])[0]
|
||||
self.toppane.title2_frame.layout_select.config(text = layname)
|
||||
schemname = os.path.splitext(os.path.split(self.schematic)[1])[0]
|
||||
self.toppane.title2_frame.schem_select.config(text = schemname)
|
||||
|
||||
# Update schematic button
|
||||
if os.path.isfile(rootpath + '/verilog/' + self.schematic + '.v'):
|
||||
if os.path.splitext(self.schematic)[1] == '.v':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
|
||||
else:
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:')
|
||||
|
||||
# Update layout button
|
||||
if os.path.splitext(self.layout)[1] == '.mag':
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Layout:')
|
||||
else:
|
||||
self.toppane.title2_frame.schem_label.config(text = 'Layout netlist:')
|
||||
|
||||
# If there is a comparison file that post-dates both netlists, load it.
|
||||
self.check_lvs()
|
||||
return True
|
||||
@@ -567,8 +585,8 @@ class LVSManager(ttk.Frame):
|
||||
# load LVS results from comp.json
|
||||
project_path = self.rootpath
|
||||
project_name = self.project
|
||||
layout_path = project_path + '/layout/' + project_name + '.spc'
|
||||
net_path = project_path + '/netlist/' + project_name + '.spi'
|
||||
layout_path = project_path + '/layout/' + project_name + '.spice'
|
||||
net_path = project_path + '/netlist/' + project_name + '.spice'
|
||||
comp_path = project_path + '/layout/comp.json'
|
||||
|
||||
if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path):
|
||||
@@ -599,7 +617,7 @@ class LVSManager(ttk.Frame):
|
||||
mproc.stdin.write("ext2spice cthresh infinite\n")
|
||||
mproc.stdin.write("ext2spice rthresh infinite\n")
|
||||
mproc.stdin.write("ext2spice blackbox on\n")
|
||||
mproc.stdin.write("ext2spice -o " + self.layout + ".spi\n")
|
||||
mproc.stdin.write("ext2spice -o " + self.layout + ".spice\n")
|
||||
mproc.stdin.write("quit -noprompt\n")
|
||||
magicout = mproc.communicate()[0]
|
||||
self.printwarn(magicout)
|
||||
@@ -607,8 +625,8 @@ class LVSManager(ttk.Frame):
|
||||
print('Failure to generate new layout netlist.')
|
||||
return False
|
||||
|
||||
# Move .spi netlist to project_dir/netlist/lvs/
|
||||
shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path)
|
||||
# Move .spice netlist to project_dir/netlist/lvs/
|
||||
shutil.move(project_path + '/layout/' + self.layout + '.spice', layout_path)
|
||||
# Remove extraction files
|
||||
for file in os.listdir(project_path + '/layout'):
|
||||
if os.path.splitext(file)[1] == '.ext':
|
||||
@@ -625,17 +643,37 @@ class LVSManager(ttk.Frame):
|
||||
|
||||
project_path = self.rootpath
|
||||
project_name = self.project
|
||||
comp_path = project_path + '/layout/comp.json'
|
||||
|
||||
# Diagnostic:
|
||||
print('project_name is ' + project_name)
|
||||
print('project_path is ' + project_path)
|
||||
print('self.layout is ' + self.layout)
|
||||
print('self.schematic is ' + self.schematic)
|
||||
print('self.lvs_setup is ' + self.lvs_setup)
|
||||
|
||||
has_vlog = False
|
||||
vlog_path = project_path + '/verilog/' + project_name + '.v'
|
||||
|
||||
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi'
|
||||
net_path = project_path + '/netlist/schem/' + self.schematic + '.spi'
|
||||
layout_src = project_path + '/layout/' + self.layout + '.mag'
|
||||
if os.path.isfile(self.layout):
|
||||
layout_path = self.layout
|
||||
layout_src = None
|
||||
else:
|
||||
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spice'
|
||||
layout_src = project_path + '/layout/' + self.layout + '.mag'
|
||||
|
||||
comp_dir = os.path.split(layout_path)[0]
|
||||
comp_path = comp_dir + '/comp.json'
|
||||
|
||||
if os.path.isfile(self.schematic):
|
||||
net_path = self.schematic
|
||||
else:
|
||||
net_path = project_path + '/netlist/schem/' + self.schematic + '.spice'
|
||||
|
||||
# Does the setup file exist (this is optional)?
|
||||
if self.lvs_setup != '' and not os.path.isfile('setup.tcl'):
|
||||
if self.lvs_setup == '' and not os.path.isfile('setup.tcl'):
|
||||
print('No technology setup file selected.')
|
||||
elif not os.path.isfile(self.lvs_setup):
|
||||
print("Can't find technology setup file " + self.lvs_setup)
|
||||
|
||||
# Does schematic netlist exist?
|
||||
if not os.path.isfile(vlog_path) and not os.path.isfile(net_path):
|
||||
@@ -643,14 +681,16 @@ class LVSManager(ttk.Frame):
|
||||
return
|
||||
|
||||
# Does LVS netlist subdirectory exist?
|
||||
if not os.path.exists(project_path + '/netlist/lvs'):
|
||||
os.makedirs(project_path + '/netlist/lvs')
|
||||
if os.path.exists(project_path + '/netlist'):
|
||||
if not os.path.exists(project_path + '/netlist/lvs'):
|
||||
os.makedirs(project_path + '/netlist/lvs')
|
||||
|
||||
# Does layout netlist exist and is it current?
|
||||
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
|
||||
return False
|
||||
if layout_src:
|
||||
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
|
||||
return False
|
||||
|
||||
# Final check
|
||||
# Final checks
|
||||
if not os.path.isfile(layout_path):
|
||||
print('Error: No netlist generated from magic.')
|
||||
return
|
||||
@@ -662,26 +702,27 @@ class LVSManager(ttk.Frame):
|
||||
with open(layout_path) as ifile:
|
||||
spitext = ifile.read()
|
||||
|
||||
# spilines = spitext.replace(',', '|')
|
||||
# with open(layout_path, 'w') as ofile:
|
||||
# ofile.write(spilines)
|
||||
|
||||
# Check the netlist to see if the cell to match is a subcircuit. If
|
||||
# not, then assume it is the top level.
|
||||
|
||||
layoutcell = os.path.splitext(os.path.split(self.layout)[1])[0]
|
||||
is_subckt = False
|
||||
subname = None
|
||||
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
|
||||
dutlines = spitext.replace('\n+', ' ').splitlines()
|
||||
for line in dutlines:
|
||||
lmatch = subrex.match(line)
|
||||
if lmatch:
|
||||
subname = lmatch.group(1)
|
||||
if subname == self.layout:
|
||||
if subname == layoutcell:
|
||||
is_subckt = True
|
||||
break
|
||||
|
||||
if is_subckt:
|
||||
layout_arg = layout_path + ' ' + self.layout
|
||||
layout_arg = self.layout + ' ' + layoutcell
|
||||
layout_text = '"' + layout_arg + '"'
|
||||
elif subname:
|
||||
layout_arg = self.layout + ' ' + subname
|
||||
layout_text = '"' + layout_arg + '"'
|
||||
else:
|
||||
layout_arg = layout_path
|
||||
@@ -690,8 +731,39 @@ class LVSManager(ttk.Frame):
|
||||
if has_vlog:
|
||||
schem_arg = vlog_path + ' ' + self.schematic
|
||||
else:
|
||||
schem_arg = net_path + ' ' + self.schematic
|
||||
schem_text = '"' + schem_arg + '"'
|
||||
# Final checks
|
||||
if not os.path.isfile(net_path):
|
||||
print('Error: No netlist from schematic.')
|
||||
return
|
||||
|
||||
with open(net_path) as ifile:
|
||||
spitext = ifile.read()
|
||||
|
||||
# Check the netlist to see if the cell to match is a subcircuit. If
|
||||
# not, then assume it is the top level.
|
||||
|
||||
schemcell = os.path.splitext(os.path.split(self.schematic)[1])[0]
|
||||
subname = None
|
||||
is_subckt = False
|
||||
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
|
||||
dutlines = spitext.replace('\n+', ' ').splitlines()
|
||||
for line in dutlines:
|
||||
lmatch = subrex.match(line)
|
||||
if lmatch:
|
||||
subname = lmatch.group(1)
|
||||
if subname == schemcell:
|
||||
is_subckt = True
|
||||
break
|
||||
|
||||
if is_subckt:
|
||||
schem_arg = self.schematic + ' ' + schemcell
|
||||
schem_text = '"' + schem_arg + '"'
|
||||
elif subname:
|
||||
schem_arg = self.schematic + ' ' + subname
|
||||
schem_text = '"' + schem_arg + '"'
|
||||
else:
|
||||
schem_arg = net_path
|
||||
schem_text = schem_arg
|
||||
|
||||
# Remove any previous comparison output file
|
||||
comp_out_path = os.path.splitext(comp_path)[0] + '.out'
|
||||
@@ -708,7 +780,7 @@ class LVSManager(ttk.Frame):
|
||||
self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs',
|
||||
layout_arg, schem_arg,
|
||||
self.lvs_setup, comp_out_path, '-json', '-blackbox'],
|
||||
cwd=project_path + '/layout',
|
||||
cwd=comp_dir,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
|
||||
# This is largely unnecessary as netgen usually runs to completion very quickly.
|
||||
self.watchclock(comp_path)
|
||||
|
||||
+96
-12
@@ -352,7 +352,7 @@ proc netgen::convert_to_json {filename lvs_final} {
|
||||
close $fjson
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------
|
||||
#-----------------------------------------------------------------------
|
||||
# Define the "lvs" command as a way of calling the netgen options
|
||||
# for standard compare, essentially the same as the old "netcomp"
|
||||
# standalone program.
|
||||
@@ -361,14 +361,20 @@ proc netgen::convert_to_json {filename lvs_final} {
|
||||
# although if the cells have not been read in yet, then the
|
||||
# original syntax of filename or {filename cellname} is required.
|
||||
#
|
||||
# "args" is passed to verify and may therefore contain only the
|
||||
# value "-list" or nothing. If "-list", then output is returned
|
||||
# as a nested list.
|
||||
#----------------------------------------------------------------
|
||||
# "args" may be "-list", "-json", or "-blackbox".
|
||||
# "-list" returns output as a nested list.
|
||||
# "-json" creates a .json-format output file in addition to stdout.
|
||||
# "-blackbox" treats empty cells as black-box entries.
|
||||
# "-noflatten={list}" is a list of cells not to flatten if mismatched.
|
||||
# i.e., the cells are expected to match and any mismatch cannot be
|
||||
# expected to be resolved by flattening the contents of the mismatched
|
||||
# cells.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
set dolist 0
|
||||
set dojson 0
|
||||
set noflat {}
|
||||
foreach arg $args {
|
||||
if {$arg == "-list"} {
|
||||
puts stdout "Generating list result"
|
||||
@@ -382,6 +388,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
} elseif {$arg == "-blackbox"} {
|
||||
puts stdout "Treating empty subcircuits as black-box cells"
|
||||
netgen::model blackbox on
|
||||
} elseif {[string first "-noflatten=" $arg] == 0} {
|
||||
set value [string range $arg 11 end]
|
||||
# If argument is a filename then read the list of cells from it;
|
||||
# otherwise, argument is the list of files itself in quotes or
|
||||
# braces.
|
||||
if {![catch {file exists $value}]} {
|
||||
if {![catch {open $value r} fnf]} {
|
||||
while {[gets $fnf line] >= 0} {
|
||||
if {[lindex $line 0] != "#"} {
|
||||
foreach cell $line {
|
||||
lappend noflat $cell
|
||||
}
|
||||
}
|
||||
}
|
||||
close $fnf
|
||||
} else {
|
||||
puts stderr "Cannot open file $value for reading cell list."
|
||||
}
|
||||
} else {
|
||||
set noflat [string trim $value \"\{\}]
|
||||
}
|
||||
if {[llength $noflat] > 0} {
|
||||
puts stdout "Will not flatten these subcells: $noflat"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +531,8 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
return
|
||||
}
|
||||
set properr {}
|
||||
set matcherr {}
|
||||
set pinsgood 0
|
||||
while {$endval != {}} {
|
||||
if {$dolist == 1} {
|
||||
netgen::run -list converge
|
||||
@@ -528,9 +560,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
|
||||
# Flatten the non-matching subcircuit (but not the top-level cells)
|
||||
if {[netgen::print queue] != {}} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
if {([lsearch $noflat [lindex $endval 0]] == -1) &&
|
||||
([lsearch $noflat [lindex $endval 1]] == -1)} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
} else {
|
||||
netgen::log put " Continuing with black-boxed subcircuits $endval"
|
||||
lappend matcherr [lindex $endval 0]
|
||||
# Match pins
|
||||
netgen::log echo off
|
||||
if {$dolist == 1} {
|
||||
set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \
|
||||
"$fnum2 [lindex $endval 1]"]
|
||||
} else {
|
||||
set result [equate -force pins "$fnum1 [lindex $endval 0]" \
|
||||
"$fnum2 [lindex $endval 1]"]
|
||||
}
|
||||
if {$result != 0} {
|
||||
equate classes "$fnum1 [lindex $endval 0]" \
|
||||
"$fnum2 [lindex $endval 1]"
|
||||
}
|
||||
set pinsgood $result
|
||||
netgen::log echo on
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# Match pins
|
||||
@@ -546,15 +599,39 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
equate classes "$fnum1 [lindex $endval 0]" \
|
||||
"$fnum2 [lindex $endval 1]"
|
||||
}
|
||||
set pinsgood $result
|
||||
netgen::log echo on
|
||||
}
|
||||
if {$uresult == 2} {lappend properr [lindex $endval 0]}
|
||||
} else {
|
||||
# Flatten the non-matching subcircuit (but not the top-level cells)
|
||||
if {[netgen::print queue] != {}} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
if {([lsearch $noflat [lindex $endval 1]] == -1) &&
|
||||
([lsearch $noflat [lindex $endval 1]] == -1)} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
} else {
|
||||
netgen::log put " Continuing with black-boxed subcircuits $endval"
|
||||
lappend matcherr [lindex $endval 0]
|
||||
netgen::log put " Continuing with black-boxed subcircuits $endval"
|
||||
lappend matcherr [lindex $endval 0]
|
||||
# Match pins
|
||||
netgen::log echo off
|
||||
if {$dolist == 1} {
|
||||
set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \
|
||||
"$fnum2 [lindex $endval 1]"]
|
||||
} else {
|
||||
set result [equate -force pins "$fnum1 [lindex $endval 0]" \
|
||||
"$fnum2 [lindex $endval 1]"]
|
||||
}
|
||||
if {$result != 0} {
|
||||
equate classes "$fnum1 [lindex $endval 0]" \
|
||||
"$fnum2 [lindex $endval 1]"
|
||||
}
|
||||
set pinsgood $result
|
||||
netgen::log echo on
|
||||
}
|
||||
}
|
||||
}
|
||||
netgen::log echo off
|
||||
@@ -569,10 +646,17 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
netgen::log echo off
|
||||
puts stdout "Result: " nonewline
|
||||
netgen::log echo on
|
||||
verify only
|
||||
if {$pinsgood == 0} {
|
||||
netgen::log put "The top level cell failed pin matching.\n"
|
||||
} else {
|
||||
verify only
|
||||
}
|
||||
if {$properr != {}} {
|
||||
netgen::log put "The following cells had property errors: $properr\n"
|
||||
}
|
||||
if {$matcherr != {}} {
|
||||
netgen::log put "The following subcells failed to match: $matcherr\n"
|
||||
}
|
||||
if {$dolog} {
|
||||
netgen::log end
|
||||
}
|
||||
|
||||
+112
-20
@@ -94,6 +94,7 @@ int _netcmp_ignore(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
int _netcmp_permute(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
int _netcmp_property(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
int _netcmp_exhaustive(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
int _netcmp_symmetry(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
int _netcmp_restart(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
int _netcmp_global(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
int _netcmp_convert(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
|
||||
@@ -244,6 +245,8 @@ Command netcmp_cmds[] = {
|
||||
{"exhaustive", _netcmp_exhaustive,
|
||||
"\n "
|
||||
"toggle exhaustive subdivision"},
|
||||
{"symmetry", _netcmp_symmetry,
|
||||
"(deprecated)"},
|
||||
{"restart", _netcmp_restart,
|
||||
"\n "
|
||||
"start over (reset data structures)"},
|
||||
@@ -1532,23 +1535,24 @@ int
|
||||
_netgen_model(ClientData clientData,
|
||||
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
|
||||
{
|
||||
struct nlist *tp;
|
||||
struct nlist *tp, *tp2;
|
||||
char *model, *retclass;
|
||||
unsigned char class;
|
||||
int fnum = -1;
|
||||
int result, index, nports;
|
||||
int result, index, nports, nports2;
|
||||
|
||||
char *modelclasses[] = {
|
||||
"undefined", "nmos", "pmos", "pnp", "npn",
|
||||
"resistor", "capacitor", "diode",
|
||||
"inductor", "module", "blackbox", "xline",
|
||||
"moscap", "mosfet", "bjt", "subcircuit", NULL
|
||||
"moscap", "mosfet", "bjt", "subcircuit", "copy",
|
||||
NULL
|
||||
};
|
||||
enum OptionIdx {
|
||||
UNDEF_IDX, NMOS_IDX, PMOS_IDX, PNP_IDX, NPN_IDX,
|
||||
RES_IDX, CAP_IDX, DIODE_IDX, INDUCT_IDX,
|
||||
MODULE_IDX, BLACKBOX_IDX, XLINE_IDX, MOSCAP_IDX,
|
||||
MOSFET_IDX, BJT_IDX, SUBCKT_IDX
|
||||
MOSFET_IDX, BJT_IDX, SUBCKT_IDX, COPY_IDX
|
||||
};
|
||||
|
||||
if (objc != 3 && objc != 2) {
|
||||
@@ -1580,7 +1584,7 @@ _netgen_model(ClientData clientData,
|
||||
|
||||
if (objc == 3) {
|
||||
model = Tcl_GetString(objv[2]);
|
||||
nports = NumberOfPorts(model);
|
||||
nports = NumberOfPorts(model, fnum);
|
||||
|
||||
if (Tcl_GetIndexFromObj(interp, objv[2], (CONST84 char **)modelclasses,
|
||||
"class", 0, &index) != TCL_OK) {
|
||||
@@ -1644,6 +1648,40 @@ _netgen_model(ClientData clientData,
|
||||
break;
|
||||
case SUBCKT_IDX:
|
||||
class = CLASS_SUBCKT;
|
||||
break;
|
||||
case COPY_IDX:
|
||||
/* "copy" is not a class, but indicates that the cell, */
|
||||
/* if undefined or a module, should have its class */
|
||||
/* taken from the other circuit, if that circuit has a */
|
||||
/* cell of the same name. */
|
||||
if (Circuit1 == NULL || Circuit2 == NULL) {
|
||||
Tcl_SetResult(interp, "Circuits have not been queued for comparison.",
|
||||
NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if (tp == Circuit1) {
|
||||
tp2 = LookupCellFile(tp->name, Circuit2->file);
|
||||
nports2 = NumberOfPorts(tp2->name, Circuit2->file);
|
||||
}
|
||||
else if (tp == Circuit2) {
|
||||
tp2 = LookupCellFile(tp->name, Circuit1->file);
|
||||
nports2 = NumberOfPorts(tp2->name, Circuit1->file);
|
||||
}
|
||||
else {
|
||||
Tcl_SetResult(interp, "The referenced netlist is not being compared.",
|
||||
NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
/* Should a non-matching number of ports be considered a fatal error? */
|
||||
// if (nports2 != nports) {
|
||||
// Tcl_SetResult(interp, "The number of ports for this cell does not "
|
||||
// "match between netlists.", NULL);
|
||||
// return TCL_ERROR;
|
||||
// }
|
||||
|
||||
class = tp2->class;
|
||||
/* To do (maybe): Rename tp ports to match tp2? */
|
||||
|
||||
break;
|
||||
}
|
||||
tp->class = class;
|
||||
@@ -2390,6 +2428,8 @@ _netcmp_run(ClientData clientData,
|
||||
else {
|
||||
enable_interrupt();
|
||||
while (!Iterate() && !InterruptPending);
|
||||
ExhaustiveSubdivision = 1;
|
||||
while (!Iterate() && !InterruptPending);
|
||||
if (dolist) {
|
||||
result = _netcmp_verify(clientData, interp, 2, objv - 1);
|
||||
}
|
||||
@@ -2408,6 +2448,8 @@ _netcmp_run(ClientData clientData,
|
||||
else {
|
||||
enable_interrupt();
|
||||
while (!Iterate() && !InterruptPending);
|
||||
ExhaustiveSubdivision = 1;
|
||||
while (!Iterate() && !InterruptPending);
|
||||
automorphisms = VerifyMatching();
|
||||
if (automorphisms == -1)
|
||||
Fprintf(stdout, "Netlists do not match.\n");
|
||||
@@ -2419,8 +2461,7 @@ _netcmp_run(ClientData clientData,
|
||||
automorphisms = ResolveAutomorphsByProperty();
|
||||
if (automorphisms == 0)
|
||||
Fprintf(stdout, "Netlists match uniquely.\n");
|
||||
else {
|
||||
|
||||
else if (automorphisms > 0) {
|
||||
// Next, attempt to resolve automorphisms uniquely by
|
||||
// using the pin names
|
||||
automorphisms = ResolveAutomorphsByPin();
|
||||
@@ -2428,14 +2469,17 @@ _netcmp_run(ClientData clientData,
|
||||
|
||||
if (automorphisms == 0)
|
||||
Fprintf(stdout, "Netlists match uniquely.\n");
|
||||
else
|
||||
else if (automorphisms > 0) {
|
||||
// Anything left is truly indistinguishable
|
||||
Fprintf(stdout, "Netlists match with %d symmetr%s.\n",
|
||||
automorphisms, (automorphisms == 1) ? "y" : "ies");
|
||||
|
||||
while ((automorphisms = ResolveAutomorphisms()) > 0);
|
||||
if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n");
|
||||
else Fprintf(stdout, "Circuits match correctly.\n");
|
||||
while ((automorphisms = ResolveAutomorphisms()) > 0);
|
||||
}
|
||||
if (automorphisms == -1)
|
||||
Fprintf(stdout, "Netlists do not match.\n");
|
||||
else
|
||||
Fprintf(stdout, "Circuits match correctly.\n");
|
||||
}
|
||||
if (PropertyErrorDetected) {
|
||||
Fprintf(stdout, "There were property errors.\n");
|
||||
@@ -2789,11 +2833,13 @@ _netcmp_equate(ClientData clientData,
|
||||
char *name1 = NULL, *name2 = NULL, *optstart;
|
||||
struct nlist *tp1, *tp2, *SaveC1, *SaveC2;
|
||||
struct objlist *ob1, *ob2;
|
||||
struct ElementClass *saveEclass = NULL;
|
||||
struct NodeClass *saveNclass = NULL;
|
||||
int file1, file2;
|
||||
int i, l1, l2, ltest, lent, dolist = 0;
|
||||
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
|
||||
Tcl_Obj *tobj1, *tobj2, *tobj3;
|
||||
|
||||
if (objc > 1) {
|
||||
while (objc > 1) {
|
||||
optstart = Tcl_GetString(objv[1]);
|
||||
if (*optstart == '-') optstart++;
|
||||
if (!strcmp(optstart, "list")) {
|
||||
@@ -2801,6 +2847,13 @@ _netcmp_equate(ClientData clientData,
|
||||
objv++;
|
||||
objc--;
|
||||
}
|
||||
else if (!strcmp(optstart, "force")) {
|
||||
doforce = 1;
|
||||
objv++;
|
||||
objc--;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if ((objc != 2) && (objc != 4) && (objc != 6)) {
|
||||
@@ -2958,6 +3011,12 @@ _netcmp_equate(ClientData clientData,
|
||||
break;
|
||||
|
||||
case PINS_IDX:
|
||||
if ((ElementClasses != NULL) && (doforce == TRUE)) {
|
||||
saveEclass = ElementClasses;
|
||||
saveNclass = NodeClasses;
|
||||
ElementClasses = NULL;
|
||||
NodeClasses = NULL;
|
||||
}
|
||||
if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) {
|
||||
if (CurrentCell == NULL) {
|
||||
Fprintf(stderr, "Equate elements: no current cell.\n");
|
||||
@@ -2982,8 +3041,7 @@ _netcmp_equate(ClientData clientData,
|
||||
else {
|
||||
Fprintf(stdout, "Equate pins: cell %s and/or %s "
|
||||
"has no elements.\n", name1, name2);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
|
||||
return TCL_OK;
|
||||
/* This is not necessarily an error, so go ahead and match pins. */
|
||||
}
|
||||
}
|
||||
if (ElementClasses == NULL) {
|
||||
@@ -3021,6 +3079,12 @@ _netcmp_equate(ClientData clientData,
|
||||
/* Recover temporarily set global variables (see above) */
|
||||
Circuit1 = SaveC1;
|
||||
Circuit2 = SaveC2;
|
||||
|
||||
/* Recover ElementClasses if forcing pins on mismatched circuits */
|
||||
if (doforce == TRUE) {
|
||||
ElementClasses = saveEclass;
|
||||
NodeClasses = saveNclass;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3283,10 +3347,10 @@ _netcmp_property(ClientData clientData,
|
||||
int result, index, idx2;
|
||||
|
||||
char *suboptions[] = {
|
||||
"integer", "double", "value", "string", NULL
|
||||
"integer", "double", "value", "string", "expression", NULL
|
||||
};
|
||||
enum SubOptionIdx {
|
||||
INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX
|
||||
INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX, EXPRESSION_IDX
|
||||
};
|
||||
|
||||
/* Note: "merge" has been deprecated, but kept for backwards compatibility. */
|
||||
@@ -3380,9 +3444,15 @@ _netcmp_property(ClientData clientData,
|
||||
GlobalParallelNone = FALSE;
|
||||
SetParallelCombine(TRUE);
|
||||
}
|
||||
else if (!strcmp(Tcl_GetString(objv[2]), "connected")) {
|
||||
GlobalParallelOpen = FALSE;
|
||||
}
|
||||
else if (!strcmp(Tcl_GetString(objv[2]), "open")) {
|
||||
GlobalParallelOpen = TRUE;
|
||||
}
|
||||
else {
|
||||
Tcl_SetResult(interp, "Bad option, should be property parallel none|all",
|
||||
NULL);
|
||||
Tcl_SetResult(interp, "Bad option, should be property parallel "
|
||||
"none|all|connected", NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
@@ -3669,6 +3739,11 @@ _netcmp_property(ClientData clientData,
|
||||
PropertyString(tp->name, fnum,
|
||||
Tcl_GetString(tobj1), ival, NULL);
|
||||
break;
|
||||
case EXPRESSION_IDX:
|
||||
PropertyString(tp->name, fnum,
|
||||
Tcl_GetString(tobj1), 0,
|
||||
Tcl_GetString(tobj3));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3938,6 +4013,23 @@ _netcmp_permute(ClientData clientData,
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Function name: _netcmp_symmetry */
|
||||
/* Syntax: netgen::symmetry [fast|full] */
|
||||
/* Formerly: x */
|
||||
/* Results: */
|
||||
/* Side Effects: */
|
||||
/* Notes: Deprecated, retained for compatibility. */
|
||||
/*------------------------------------------------------*/
|
||||
|
||||
int
|
||||
_netcmp_symmetry(ClientData clientData,
|
||||
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
|
||||
{
|
||||
Printf("Symmetry breaking method has been deprecated.\n");
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------*/
|
||||
/* Function name: _netcmp_exhaustive */
|
||||
/* Syntax: netgen::exhaustive [on|off] */
|
||||
@@ -3959,7 +4051,7 @@ _netcmp_exhaustive(ClientData clientData,
|
||||
int result, index;
|
||||
|
||||
if (objc == 1)
|
||||
index = YES_IDX;
|
||||
index = -1;
|
||||
else {
|
||||
if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)yesno,
|
||||
"option", 0, &index) != TCL_OK)
|
||||
|
||||
Reference in New Issue
Block a user