mirror of
https://github.com/RTimothyEdwards/netgen.git
synced 2026-08-22 14:07:07 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -12,3 +12,5 @@ scripts/config.status
|
|||||||
*~
|
*~
|
||||||
*.log
|
*.log
|
||||||
UPDATE_ME
|
UPDATE_ME
|
||||||
|
tcltk/netgen.sh
|
||||||
|
tcltk/netgen.tcl
|
||||||
|
|||||||
+316
-208
@@ -193,17 +193,19 @@ void flattenCell(char *name, int file)
|
|||||||
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
|
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
|
||||||
FreeString(tmp->name);
|
FreeString(tmp->name);
|
||||||
tmp->name = strsave(tmpstr);
|
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));
|
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||||
if (tmp->type == FIRSTPIN)
|
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
|
||||||
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
|
#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 */
|
/* splice instance out of parent */
|
||||||
@@ -252,9 +254,10 @@ void flattenCell(char *name, int file)
|
|||||||
int flattenInstancesOf(char *name, int fnum, char *instance)
|
int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||||
{
|
{
|
||||||
struct objlist *ParentParams;
|
struct objlist *ParentParams;
|
||||||
struct objlist *ParentProps;
|
struct objlist *ParentProps, *CurrentProp;
|
||||||
struct objlist *NextObj;
|
struct objlist *NextObj, *LastObj, *prepp;
|
||||||
struct objlist *ChildObjList;
|
struct objlist *ChildObjList, *ChildListEnd;
|
||||||
|
struct objlist *ChildStart, *ChildEnd, *ParentEnd;
|
||||||
struct nlist *ThisCell;
|
struct nlist *ThisCell;
|
||||||
struct nlist *ChildCell;
|
struct nlist *ChildCell;
|
||||||
struct objlist *tmp, *ob2, *ob3;
|
struct objlist *tmp, *ob2, *ob3;
|
||||||
@@ -296,21 +299,35 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
|||||||
while (notdone) {
|
while (notdone) {
|
||||||
notdone = 0;
|
notdone = 0;
|
||||||
ParentParams = ThisCell->cell;
|
ParentParams = ThisCell->cell;
|
||||||
|
LastObj = NULL;
|
||||||
|
|
||||||
for (ParentParams = ThisCell->cell; ParentParams != NULL;
|
for (ParentParams = ThisCell->cell; ParentParams != NULL;
|
||||||
ParentParams = NextObj) {
|
ParentParams = NextObj) {
|
||||||
if (Debug) Printf("Parent = %s, type = %d\n",
|
if (Debug) Printf("Parent = %s, type = %d\n",
|
||||||
ParentParams->name, ParentParams->type);
|
ParentParams->name, ParentParams->type);
|
||||||
NextObj = ParentParams->next;
|
NextObj = ParentParams->next;
|
||||||
if (ParentParams->type != FIRSTPIN) continue;
|
if (ParentParams->type != FIRSTPIN) {
|
||||||
if (!(*matchfunc)(ParentParams->model.class, instance)) continue;
|
LastObj = ParentParams;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(*matchfunc)(ParentParams->model.class, instance)) {
|
||||||
|
LastObj = ParentParams;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file);
|
ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file);
|
||||||
if (Debug)
|
if (Debug)
|
||||||
Printf(" Flattening instance: %s, primitive = %s\n",
|
Printf(" Flattening instance: %s, primitive = %s\n",
|
||||||
ParentParams->instance.name, (ChildCell->class ==
|
ParentParams->instance.name, (ChildCell->class ==
|
||||||
CLASS_SUBCKT) ? "no" : "yes");
|
CLASS_SUBCKT) ? "no" : "yes");
|
||||||
if (ChildCell->class != CLASS_SUBCKT) continue;
|
if ((ChildCell->class != CLASS_SUBCKT) && (ChildCell->class != CLASS_MODULE)) {
|
||||||
if (ChildCell == ThisCell) continue; // Avoid infinite loop
|
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 */
|
/* Does the parent cell have properties? If so, save a pointer to them */
|
||||||
for (ParentProps = ParentParams->next; ParentProps &&
|
for (ParentProps = ParentParams->next; ParentProps &&
|
||||||
@@ -318,202 +335,250 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
|||||||
ParentProps = ParentProps->next) {
|
ParentProps = ParentProps->next) {
|
||||||
if (ParentProps->type == PROPERTY) break;
|
if (ParentProps->type == PROPERTY) break;
|
||||||
}
|
}
|
||||||
if (ParentProps && (ParentProps->type != PROPERTY)) ParentProps = NULL;
|
if (ParentProps && (ParentProps->type != PROPERTY)) {
|
||||||
|
ParentProps = NULL;
|
||||||
/* not primitive, so need to flatten this instance */
|
CurrentProp = NULL;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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 && ParentEnd->next->type != FIRSTPIN;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for each element in child, prepend 'prefix' */
|
||||||
#if !OLDPREFIX
|
#if !OLDPREFIX
|
||||||
/* replaces all the sprintf's below */
|
/* replaces all the sprintf's below */
|
||||||
strcpy(tmpstr,ParentParams->instance.name);
|
strcpy(tmpstr,ParentParams->instance.name);
|
||||||
strcat(tmpstr,SEPARATOR);
|
strcat(tmpstr,SEPARATOR);
|
||||||
prefixlength = strlen(tmpstr);
|
prefixlength = strlen(tmpstr);
|
||||||
#endif
|
#endif
|
||||||
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) {
|
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) {
|
||||||
if (tmp->type == PROPERTY)
|
if (tmp->type == PROPERTY)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
else if (IsGlobal(tmp)) {
|
else if (IsGlobal(tmp)) {
|
||||||
/* Keep the name but search for node of same name in parent */
|
/* Keep the name but search for node of same name in parent */
|
||||||
/* and replace the node number, if found. */
|
/* and replace the node number, if found. */
|
||||||
|
|
||||||
for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) {
|
for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) {
|
||||||
/* Type in parent may be a port, not a global */
|
/* Type in parent may be a port, not a global */
|
||||||
if (ob2->type == tmp->type || ob2->type == PORT) {
|
if (ob2->type == tmp->type || ob2->type == PORT) {
|
||||||
if ((*matchfunc)(tmp->name, ob2->name)) {
|
if ((*matchfunc)(tmp->name, ob2->name)) {
|
||||||
if (ob2->node >= 0) {
|
if (ob2->node >= 0) {
|
||||||
// Replace all child objects with this node number
|
// Replace all child objects with this node number
|
||||||
rnodenum = tmp->node;
|
rnodenum = tmp->node;
|
||||||
for (ob3 = ChildObjList; ob3 != NULL; ob3 = ob3->next) {
|
for (ob3 = ChildStart; ob3 != NULL; ob3 = ob3->next) {
|
||||||
if (ob3->node == rnodenum)
|
if (ob3->node == rnodenum)
|
||||||
ob3->node = ob2->node;
|
ob3->node = ob2->node;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't hash this if the parent had a port of this name
|
// Don't hash this if the parent had a port of this name
|
||||||
if (!ob2 || ob2->type != PORT)
|
if (!ob2 || ob2->type != PORT)
|
||||||
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OLDPREFIX
|
#if OLDPREFIX
|
||||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||||
tmp->name);
|
tmp->name);
|
||||||
#else
|
#else
|
||||||
strcpy(tmpstr+prefixlength,tmp->name);
|
strcpy(tmpstr+prefixlength,tmp->name);
|
||||||
#endif
|
#endif
|
||||||
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
|
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
|
||||||
FreeString(tmp->name);
|
FreeString(tmp->name);
|
||||||
tmp->name = strsave(tmpstr);
|
tmp->name = strsave(tmpstr);
|
||||||
|
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
||||||
|
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
|
||||||
#if OLDPREFIX
|
#if OLDPREFIX
|
||||||
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
|
||||||
tmp->instance.name);
|
tmp->instance.name);
|
||||||
#else
|
#else
|
||||||
strcpy(tmpstr+prefixlength,tmp->instance.name);
|
strcpy(tmpstr+prefixlength,tmp->instance.name);
|
||||||
#endif
|
#endif
|
||||||
FreeString(tmp->instance.name);
|
FreeString(tmp->instance.name);
|
||||||
tmp->instance.name = strsave(tmpstr);
|
tmp->instance.name = strsave(tmpstr);
|
||||||
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
|
if (tmp->type == FIRSTPIN)
|
||||||
if (tmp->type == FIRSTPIN)
|
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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->type != PROPERTY) break;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
|
||||||
|
/* Put the child cell at the start of ChildObjList */
|
||||||
|
ChildEnd->next = ChildObjList;
|
||||||
|
ChildObjList = ChildStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* splice instance out of parent */
|
/* Put the child cell at the start of ChildObjList */
|
||||||
if ((ParentParams == ThisCell->cell) && (ChildObjList == NULL)) {
|
ChildEnd->next = ChildObjList;
|
||||||
ThisCell->cell = ob2; /* Child cell was empty */
|
ChildObjList = ChildStart;
|
||||||
tmp = ob2;
|
|
||||||
}
|
/* Pull the instance out of the parent */
|
||||||
else {
|
|
||||||
|
if ((ParentParams != ThisCell->cell) || (ChildObjList != NULL)) {
|
||||||
if (ParentParams == ThisCell->cell) {
|
if (ParentParams == ThisCell->cell) {
|
||||||
/* ParentParams are the very first thing in the list */
|
/* ParentParams are the very first thing in the list */
|
||||||
ThisCell->cell = ChildObjList;
|
ThisCell->cell = ChildObjList;
|
||||||
for (ob2 = ChildObjList; ob2 && ob2->next != NULL; ob2 = ob2->next) ;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* find ParentParams in ThisCell list */
|
/* find ParentParams in ThisCell list. In most cases, LastObj */
|
||||||
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams; ob2=ob2->next);
|
/* should be pointing to it. */
|
||||||
if (ob2)
|
if (LastObj && (LastObj->next == ParentParams)) {
|
||||||
for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ;
|
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 */
|
|
||||||
|
|
||||||
tmp = ParentParams;
|
/* Link end of child list into the parent */
|
||||||
do {
|
if (ChildListEnd)
|
||||||
tmp = tmp->next;
|
ChildListEnd->next = ParentEnd->next;
|
||||||
} while ((tmp != NULL) && ((tmp->type > FIRSTPIN) || (tmp->type == PROPERTY)));
|
|
||||||
if (ob2) ob2->next = tmp;
|
|
||||||
}
|
}
|
||||||
while (ParentParams != tmp) {
|
while (ParentParams != ChildListEnd->next) {
|
||||||
ob2 = ParentParams->next;
|
ob2 = ParentParams->next;
|
||||||
FreeObjectAndHash(ParentParams, ThisCell);
|
FreeObjectAndHash(ParentParams, ThisCell);
|
||||||
ParentParams = ob2;
|
ParentParams = ob2;
|
||||||
}
|
}
|
||||||
NextObj = ParentParams;
|
NextObj = ParentParams;
|
||||||
} /* repeat until no more instances found */
|
} /* repeat until no more instances found */
|
||||||
@@ -1136,9 +1201,10 @@ int UniquePins(char *name, int filenum)
|
|||||||
struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
||||||
{
|
{
|
||||||
struct nlist *ptr;
|
struct nlist *ptr;
|
||||||
struct objlist *ob, *obt, *lob, *nob, *firstpin;
|
struct objlist *ob, *obt, *lob, *nob, *firstpin, *pob;
|
||||||
struct nlist *tc = (struct nlist *)clientdata;
|
struct nlist *tc = (struct nlist *)clientdata;
|
||||||
int pinnum;
|
int pinnum;
|
||||||
|
char *saveinst = NULL;
|
||||||
|
|
||||||
ptr = (struct nlist *)(p->ptr);
|
ptr = (struct nlist *)(p->ptr);
|
||||||
if (tc->file != ptr->file) return NULL;
|
if (tc->file != ptr->file) return NULL;
|
||||||
@@ -1186,13 +1252,44 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
FREE(ob->name);
|
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);
|
if (ob->model.class != NULL) FREE(ob->model.class);
|
||||||
FREE(ob);
|
FREE(ob);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lob = ob;
|
if ((ob->type == PROPERTY) && (pinnum == 1))
|
||||||
ob->type = pinnum++; // Renumber pins in order
|
{
|
||||||
|
/* 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;
|
ob = nob;
|
||||||
obt = obt->next;
|
obt = obt->next;
|
||||||
@@ -1203,6 +1300,8 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
|
|||||||
HashPtrInstall(firstpin->instance.name, firstpin, &(ptr->instdict));
|
HashPtrInstall(firstpin->instance.name, firstpin, &(ptr->instdict));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (saveinst != NULL) FREE(saveinst);
|
||||||
return NULL; /* Keep the search going */
|
return NULL; /* Keep the search going */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1475,13 +1574,13 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (match) {
|
if (match) {
|
||||||
if (ecomp->cell1) {
|
if (ecomp->cell1 && (ecomp->num1 > 0)) {
|
||||||
Fprintf(stdout, "Flattening instances of %s in cell %s"
|
Fprintf(stdout, "Flattening instances of %s in cell %s"
|
||||||
" makes a better match\n", ecomp->cell1->name,
|
" makes a better match\n", ecomp->cell1->name,
|
||||||
name1);
|
name1);
|
||||||
flattenInstancesOf(name1, file1, ecomp->cell1->name);
|
flattenInstancesOf(name1, file1, ecomp->cell1->name);
|
||||||
}
|
}
|
||||||
if (ecomp->cell2) {
|
if (ecomp->cell2 && (ecomp->num2 > 0)) {
|
||||||
Fprintf(stdout, "Flattening instances of %s in cell %s"
|
Fprintf(stdout, "Flattening instances of %s in cell %s"
|
||||||
" makes a better match\n", ecomp->cell2->name,
|
" makes a better match\n", ecomp->cell2->name,
|
||||||
name2);
|
name2);
|
||||||
@@ -1660,7 +1759,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
|||||||
while (ecomp != NULL) {
|
while (ecomp != NULL) {
|
||||||
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
|
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
|
||||||
((ecomp->cell1->class == CLASS_RES) ||
|
((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;
|
int node1 = -1, node2 = -1;
|
||||||
lob = NULL;
|
lob = NULL;
|
||||||
for (ob1 = tc1->cell; ob1; ) {
|
for (ob1 = tc1->cell; ob1; ) {
|
||||||
@@ -1709,11 +1809,16 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
|||||||
tsub1->name,
|
tsub1->name,
|
||||||
tc1->name);
|
tc1->name);
|
||||||
|
|
||||||
/* merge node of endpoints */
|
/* A current source is an open, while a */
|
||||||
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
|
/* resistor or voltage source is a short. */
|
||||||
if (ob2->node == node2)
|
|
||||||
ob2->node = node1;
|
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 */
|
/* snip, snip. Excise this device */
|
||||||
if (lob == NULL) {
|
if (lob == NULL) {
|
||||||
@@ -1765,7 +1870,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
|||||||
|
|
||||||
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) &&
|
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) &&
|
||||||
((ecomp->cell2->class == CLASS_RES) ||
|
((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;
|
int node1 = -1, node2 = -1;
|
||||||
lob = NULL;
|
lob = NULL;
|
||||||
for (ob2 = tc2->cell; ob2; ) {
|
for (ob2 = tc2->cell; ob2; ) {
|
||||||
@@ -1815,9 +1921,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
|
|||||||
tc2->name);
|
tc2->name);
|
||||||
|
|
||||||
/* merge node of endpoints */
|
/* merge node of endpoints */
|
||||||
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
|
if (ecomp->cell2->class != CLASS_ISOURCE) {
|
||||||
if (ob1->node == node2)
|
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
|
||||||
ob1->node = node1;
|
if (ob1->node == node2)
|
||||||
|
ob1->node = node1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* snip, snip. Excise this device */
|
/* snip, snip. Excise this device */
|
||||||
|
|||||||
+611
-363
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,6 @@ extern struct nlist *Circuit1;
|
|||||||
extern struct nlist *Circuit2;
|
extern struct nlist *Circuit2;
|
||||||
|
|
||||||
extern int ExhaustiveSubdivision;
|
extern int ExhaustiveSubdivision;
|
||||||
extern int FastSymmetryBreaking;
|
|
||||||
|
|
||||||
extern int left_col_end;
|
extern int left_col_end;
|
||||||
extern int right_col_end;
|
extern int right_col_end;
|
||||||
|
|||||||
+619
-530
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);
|
int merge_mask);
|
||||||
extern void ResolveProperties(char *name1, int file1, char *name2, int file2);
|
extern void ResolveProperties(char *name1, int file1, char *name2, int file2);
|
||||||
extern void CopyProperties(struct objlist *obj_to, struct objlist *obj_from);
|
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 int SetPropertyDefault(struct property *, struct valuelist *);
|
||||||
extern struct objlist *LinkProperties(char *model, struct keyvalue *topptr);
|
extern struct objlist *LinkProperties(char *model, struct keyvalue *topptr);
|
||||||
extern int ReduceExpressions(struct objlist *instprop, struct objlist *parprops,
|
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 */
|
/* flatten.c */
|
||||||
extern int PrematchLists(char *, int, char *, int);
|
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 */
|
/* Define (enumerate) various device classes, largely based on SPICE */
|
||||||
/* model types, mixed with some ext/sim types. */
|
/* 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 Composition; /* direction of composition */
|
||||||
extern int UnixWildcards; /* TRUE if *,?,{},[] only; false if full REGEXP */
|
extern int UnixWildcards; /* TRUE if *,?,{},[] only; false if full REGEXP */
|
||||||
extern int GlobalParallelNone; /* If TRUE, don't parallel combine any cells */
|
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 */
|
/* magic internal flag to restrict searches to recently placed cells */
|
||||||
extern int QuickSearch;
|
extern int QuickSearch;
|
||||||
/* does re"CellDef"ing a cell add to it or overwrite it??? */
|
/* does re"CellDef"ing a cell add to it or overwrite it??? */
|
||||||
|
|||||||
+6
-3
@@ -975,7 +975,10 @@ struct objlist *CopyObjList(struct objlist *oldlist, unsigned char doforall)
|
|||||||
newob->model.class = NULL;
|
newob->model.class = NULL;
|
||||||
else
|
else
|
||||||
newob->model.class = strsave(tmp->model.class);
|
newob->model.class = strsave(tmp->model.class);
|
||||||
newob->instance.name = (tmp->instance.name) ?
|
if (newob->type == NODE)
|
||||||
|
newob->instance.flags = tmp->instance.flags;
|
||||||
|
else
|
||||||
|
newob->instance.name = (tmp->instance.name) ?
|
||||||
strsave(tmp->instance.name) : NULL;
|
strsave(tmp->instance.name) : NULL;
|
||||||
}
|
}
|
||||||
newob->node = tmp->node;
|
newob->node = tmp->node;
|
||||||
@@ -1095,8 +1098,8 @@ void FreeObject(struct objlist *ob)
|
|||||||
FREE(ob->instance.props);
|
FREE(ob->instance.props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (ob->type != NODE) {
|
||||||
/* All other records */
|
/* All other records except NODE, which uses this for flags */
|
||||||
if (ob->instance.name != NULL) FreeString(ob->instance.name);
|
if (ob->instance.name != NULL) FreeString(ob->instance.name);
|
||||||
}
|
}
|
||||||
if (ob->model.class != NULL) FreeString(ob->model.class);
|
if (ob->model.class != NULL) FreeString(ob->model.class);
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#define PROXY (0) /* Used in model.port record of ports */
|
#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 */
|
/* Lists of device properties. Order is defined at the time of */
|
||||||
/* the cell definition; values are sorted at the time instances */
|
/* the cell definition; values are sorted at the time instances */
|
||||||
/* are read. */
|
/* are read. */
|
||||||
@@ -160,6 +162,7 @@ struct objlist {
|
|||||||
union {
|
union {
|
||||||
char *name; /* unique name for the instance, or */
|
char *name; /* unique name for the instance, or */
|
||||||
/* (string) value of property for properties */
|
/* (string) value of property for properties */
|
||||||
|
int flags; /* Used by NODE type to flag isolated net */
|
||||||
struct valuelist *props; /* Property record */
|
struct valuelist *props; /* Property record */
|
||||||
} instance;
|
} instance;
|
||||||
int node; /* the electrical node number of the port/node/pin */
|
int node; /* the electrical node number of the port/node/pin */
|
||||||
|
|||||||
+12
-10
@@ -473,15 +473,6 @@ void CleanupSubcell() {
|
|||||||
SetClass(CLASS_MODULE);
|
SetClass(CLASS_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------*/
|
|
||||||
/* Structure for stacking nested subcircuit definitions */
|
|
||||||
/*------------------------------------------------------*/
|
|
||||||
|
|
||||||
struct cellstack {
|
|
||||||
char *cellname;
|
|
||||||
struct cellstack *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*------------------------------------------------------*/
|
/*------------------------------------------------------*/
|
||||||
/* Push a subcircuit name onto the stack */
|
/* 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;
|
// Blackbox (library) mode---parse only subcircuits and models;
|
||||||
// ignore all components.
|
// ignore all components.
|
||||||
|
|
||||||
@@ -1184,7 +1186,7 @@ skip_ends:
|
|||||||
else if (CountPorts(model, filenum) != 2) {
|
else if (CountPorts(model, filenum) != 2) {
|
||||||
/* Modeled device: Make sure it has the right number of ports */
|
/* Modeled device: Make sure it has the right number of ports */
|
||||||
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
|
||||||
"resistor.\n");
|
"resistor.\n", model);
|
||||||
goto baddevice;
|
goto baddevice;
|
||||||
}
|
}
|
||||||
usemodel = 1;
|
usemodel = 1;
|
||||||
|
|||||||
+90
-5
@@ -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_NAME_DELIMITERS "X///**/(**)X()"
|
||||||
#define VLOG_PIN_CHECK_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
|
// Global storage for verilog parameters
|
||||||
struct hashdict verilogparams;
|
struct hashdict verilogparams;
|
||||||
// Global storage for verilog definitions
|
// Global storage for verilog definitions
|
||||||
@@ -632,11 +636,6 @@ void CleanupModule() {
|
|||||||
/* Structure for stacking nested module definitions */
|
/* Structure for stacking nested module definitions */
|
||||||
/*------------------------------------------------------*/
|
/*------------------------------------------------------*/
|
||||||
|
|
||||||
struct cellstack {
|
|
||||||
char *cellname;
|
|
||||||
struct cellstack *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
extern void IncludeVerilog(char *, int, struct cellstack **, int);
|
extern void IncludeVerilog(char *, int, struct cellstack **, int);
|
||||||
|
|
||||||
@@ -1409,6 +1408,7 @@ skip_endmodule:
|
|||||||
char *name; // Name of port in subcell
|
char *name; // Name of port in subcell
|
||||||
char *net; // Name of net connecting to port in the parent
|
char *net; // Name of net connecting to port in the parent
|
||||||
int width; // Width of port, if port is a bus
|
int width; // Width of port, if port is a bus
|
||||||
|
char flags; // Used for marking if port was added into netlist
|
||||||
struct portelement *next;
|
struct portelement *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1535,6 +1535,7 @@ nextinst:
|
|||||||
new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement));
|
new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement));
|
||||||
new_port->name = strsave(nexttok + 1);
|
new_port->name = strsave(nexttok + 1);
|
||||||
new_port->width = -1;
|
new_port->width = -1;
|
||||||
|
new_port->flags = PORT_NOT_FOUND;
|
||||||
SkipTokComments(VLOG_DELIMITERS);
|
SkipTokComments(VLOG_DELIMITERS);
|
||||||
if (!match(nexttok, "(")) {
|
if (!match(nexttok, "(")) {
|
||||||
Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok);
|
Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok);
|
||||||
@@ -1969,6 +1970,7 @@ nextinst:
|
|||||||
obpinidx = -1;
|
obpinidx = -1;
|
||||||
while (scan != NULL) {
|
while (scan != NULL) {
|
||||||
if (match(obpinname, scan->name)) {
|
if (match(obpinname, scan->name)) {
|
||||||
|
scan->flags |= PORT_FOUND;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
scan = scan->next;
|
scan = scan->next;
|
||||||
@@ -2061,6 +2063,89 @@ nextinst:
|
|||||||
if (LookupObject(scan->net, CurrentCell) == NULL) Node(scan->net);
|
if (LookupObject(scan->net, CurrentCell) == NULL) Node(scan->net);
|
||||||
join(scan->net, obptr->name);
|
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;
|
obptr = obptr->next;
|
||||||
} while (obptr != NULL && obptr->type > FIRSTPIN);
|
} while (obptr != NULL && obptr->type > FIRSTPIN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ SCRIPTINSTALL = $(DESTDIR)${INSTALL_PYDIR}
|
|||||||
|
|
||||||
include ${NETGENDIR}/defs.mak
|
include ${NETGENDIR}/defs.mak
|
||||||
|
|
||||||
|
CLEANS += lvs_manager.py
|
||||||
|
|
||||||
main: lvs_manager.py
|
main: lvs_manager.py
|
||||||
|
|
||||||
tcl-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]
|
techbase = os.path.split(techname)[1]
|
||||||
self.tech = os.path.splitext(techbase)[0]
|
self.tech = os.path.splitext(techbase)[0]
|
||||||
self.lvs_setup = techname
|
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):
|
def choose_layout(self):
|
||||||
try:
|
try:
|
||||||
@@ -358,30 +358,37 @@ class LVSManager(ttk.Frame):
|
|||||||
title = "Choose a layout cell to compare.")
|
title = "Choose a layout cell to compare.")
|
||||||
if cellname != '':
|
if cellname != '':
|
||||||
print("Selected compare cell " + cellname)
|
print("Selected compare cell " + cellname)
|
||||||
|
self.layout = cellname
|
||||||
cellbase = os.path.split(cellname)[1]
|
cellbase = os.path.split(cellname)[1]
|
||||||
self.layout = os.path.splitext(cellbase)[0]
|
layoutname = os.path.splitext(cellbase)[0]
|
||||||
self.toppane.title2_frame.layout_select.config(text = self.layout)
|
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):
|
def choose_netlist(self):
|
||||||
try:
|
try:
|
||||||
project_path = self.rootpath
|
project_path = self.rootpath
|
||||||
initdirname = self.rootpath + '/netlist/' + self.project + '.spi'
|
initdirname = self.rootpath + '/netlist/' + self.project + '.spice'
|
||||||
except:
|
except:
|
||||||
print('Must choose a project first.')
|
print('Must choose a project first.')
|
||||||
return
|
return
|
||||||
cellname = filedialog.askopenfilename(multiple=False,
|
cellname = filedialog.askopenfilename(multiple=False,
|
||||||
initialdir = initdirname,
|
initialdir = initdirname,
|
||||||
filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")),
|
filetypes = (("Spice netlist", "*.spice"),("Verilog netlist", "*.v"),("All Files","*.*")),
|
||||||
title = "Choose a netlist to compare.")
|
title = "Choose a netlist to compare.")
|
||||||
if cellname != '':
|
if cellname != '':
|
||||||
print("Selected compare cell " + cellname)
|
print("Selected compare cell " + cellname)
|
||||||
|
self.schematic = cellname
|
||||||
cellbase = os.path.split(cellname)[1]
|
cellbase = os.path.split(cellname)[1]
|
||||||
self.schematic = os.path.splitext(cellbase)[0]
|
schematic_name = os.path.splitext(cellbase)[0]
|
||||||
self.toppane.title2_frame.schem_select.config(text = self.schematic)
|
self.toppane.title2_frame.schem_select.config(text = schematic_name)
|
||||||
fileext = os.path.splitext(cellbase)[1]
|
fileext = os.path.splitext(cellbase)[1]
|
||||||
if fileext == '.v':
|
if fileext == '.v':
|
||||||
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
|
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:')
|
self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:')
|
||||||
elif fileext == '.cdl':
|
elif fileext == '.cdl':
|
||||||
self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:')
|
self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:')
|
||||||
@@ -433,7 +440,10 @@ class LVSManager(ttk.Frame):
|
|||||||
self.logfile = None
|
self.logfile = None
|
||||||
|
|
||||||
# Put new log file called 'lvs.log' in the mag/ subdirectory
|
# 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.
|
# Print some initial information to the logfile.
|
||||||
self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'),
|
self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'),
|
||||||
doflush=True)
|
doflush=True)
|
||||||
@@ -444,15 +454,23 @@ class LVSManager(ttk.Frame):
|
|||||||
# Cell name is the same as project name initially
|
# Cell name is the same as project name initially
|
||||||
self.layout = self.project
|
self.layout = self.project
|
||||||
self.schematic = self.project
|
self.schematic = self.project
|
||||||
self.toppane.title2_frame.layout_select.config(text = self.layout)
|
layname = os.path.splitext(os.path.split(self.layout)[1])[0]
|
||||||
self.toppane.title2_frame.schem_select.config(text = self.schematic)
|
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
|
# 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:')
|
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
|
||||||
else:
|
else:
|
||||||
self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:')
|
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.
|
# If there is a comparison file that post-dates both netlists, load it.
|
||||||
self.check_lvs()
|
self.check_lvs()
|
||||||
return True
|
return True
|
||||||
@@ -567,8 +585,8 @@ class LVSManager(ttk.Frame):
|
|||||||
# load LVS results from comp.json
|
# load LVS results from comp.json
|
||||||
project_path = self.rootpath
|
project_path = self.rootpath
|
||||||
project_name = self.project
|
project_name = self.project
|
||||||
layout_path = project_path + '/layout/' + project_name + '.spc'
|
layout_path = project_path + '/layout/' + project_name + '.spice'
|
||||||
net_path = project_path + '/netlist/' + project_name + '.spi'
|
net_path = project_path + '/netlist/' + project_name + '.spice'
|
||||||
comp_path = project_path + '/layout/comp.json'
|
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):
|
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 cthresh infinite\n")
|
||||||
mproc.stdin.write("ext2spice rthresh infinite\n")
|
mproc.stdin.write("ext2spice rthresh infinite\n")
|
||||||
mproc.stdin.write("ext2spice blackbox on\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")
|
mproc.stdin.write("quit -noprompt\n")
|
||||||
magicout = mproc.communicate()[0]
|
magicout = mproc.communicate()[0]
|
||||||
self.printwarn(magicout)
|
self.printwarn(magicout)
|
||||||
@@ -607,8 +625,8 @@ class LVSManager(ttk.Frame):
|
|||||||
print('Failure to generate new layout netlist.')
|
print('Failure to generate new layout netlist.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Move .spi netlist to project_dir/netlist/lvs/
|
# Move .spice netlist to project_dir/netlist/lvs/
|
||||||
shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path)
|
shutil.move(project_path + '/layout/' + self.layout + '.spice', layout_path)
|
||||||
# Remove extraction files
|
# Remove extraction files
|
||||||
for file in os.listdir(project_path + '/layout'):
|
for file in os.listdir(project_path + '/layout'):
|
||||||
if os.path.splitext(file)[1] == '.ext':
|
if os.path.splitext(file)[1] == '.ext':
|
||||||
@@ -625,17 +643,37 @@ class LVSManager(ttk.Frame):
|
|||||||
|
|
||||||
project_path = self.rootpath
|
project_path = self.rootpath
|
||||||
project_name = self.project
|
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
|
has_vlog = False
|
||||||
vlog_path = project_path + '/verilog/' + project_name + '.v'
|
vlog_path = project_path + '/verilog/' + project_name + '.v'
|
||||||
|
|
||||||
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi'
|
if os.path.isfile(self.layout):
|
||||||
net_path = project_path + '/netlist/schem/' + self.schematic + '.spi'
|
layout_path = self.layout
|
||||||
layout_src = project_path + '/layout/' + self.layout + '.mag'
|
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)?
|
# 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.')
|
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?
|
# Does schematic netlist exist?
|
||||||
if not os.path.isfile(vlog_path) and not os.path.isfile(net_path):
|
if not os.path.isfile(vlog_path) and not os.path.isfile(net_path):
|
||||||
@@ -643,14 +681,16 @@ class LVSManager(ttk.Frame):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Does LVS netlist subdirectory exist?
|
# Does LVS netlist subdirectory exist?
|
||||||
if not os.path.exists(project_path + '/netlist/lvs'):
|
if os.path.exists(project_path + '/netlist'):
|
||||||
os.makedirs(project_path + '/netlist/lvs')
|
if not os.path.exists(project_path + '/netlist/lvs'):
|
||||||
|
os.makedirs(project_path + '/netlist/lvs')
|
||||||
|
|
||||||
# Does layout netlist exist and is it current?
|
# Does layout netlist exist and is it current?
|
||||||
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
|
if layout_src:
|
||||||
return False
|
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):
|
if not os.path.isfile(layout_path):
|
||||||
print('Error: No netlist generated from magic.')
|
print('Error: No netlist generated from magic.')
|
||||||
return
|
return
|
||||||
@@ -662,26 +702,27 @@ class LVSManager(ttk.Frame):
|
|||||||
with open(layout_path) as ifile:
|
with open(layout_path) as ifile:
|
||||||
spitext = ifile.read()
|
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
|
# Check the netlist to see if the cell to match is a subcircuit. If
|
||||||
# not, then assume it is the top level.
|
# not, then assume it is the top level.
|
||||||
|
|
||||||
|
layoutcell = os.path.splitext(os.path.split(self.layout)[1])[0]
|
||||||
is_subckt = False
|
is_subckt = False
|
||||||
|
subname = None
|
||||||
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
|
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
|
||||||
dutlines = spitext.replace('\n+', ' ').splitlines()
|
dutlines = spitext.replace('\n+', ' ').splitlines()
|
||||||
for line in dutlines:
|
for line in dutlines:
|
||||||
lmatch = subrex.match(line)
|
lmatch = subrex.match(line)
|
||||||
if lmatch:
|
if lmatch:
|
||||||
subname = lmatch.group(1)
|
subname = lmatch.group(1)
|
||||||
if subname == self.layout:
|
if subname == layoutcell:
|
||||||
is_subckt = True
|
is_subckt = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if is_subckt:
|
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 + '"'
|
layout_text = '"' + layout_arg + '"'
|
||||||
else:
|
else:
|
||||||
layout_arg = layout_path
|
layout_arg = layout_path
|
||||||
@@ -690,8 +731,39 @@ class LVSManager(ttk.Frame):
|
|||||||
if has_vlog:
|
if has_vlog:
|
||||||
schem_arg = vlog_path + ' ' + self.schematic
|
schem_arg = vlog_path + ' ' + self.schematic
|
||||||
else:
|
else:
|
||||||
schem_arg = net_path + ' ' + self.schematic
|
# Final checks
|
||||||
schem_text = '"' + schem_arg + '"'
|
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
|
# Remove any previous comparison output file
|
||||||
comp_out_path = os.path.splitext(comp_path)[0] + '.out'
|
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',
|
self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs',
|
||||||
layout_arg, schem_arg,
|
layout_arg, schem_arg,
|
||||||
self.lvs_setup, comp_out_path, '-json', '-blackbox'],
|
self.lvs_setup, comp_out_path, '-json', '-blackbox'],
|
||||||
cwd=project_path + '/layout',
|
cwd=comp_dir,
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
|
||||||
# This is largely unnecessary as netgen usually runs to completion very quickly.
|
# This is largely unnecessary as netgen usually runs to completion very quickly.
|
||||||
self.watchclock(comp_path)
|
self.watchclock(comp_path)
|
||||||
|
|||||||
+89
-14
@@ -352,7 +352,7 @@ proc netgen::convert_to_json {filename lvs_final} {
|
|||||||
close $fjson
|
close $fjson
|
||||||
}
|
}
|
||||||
|
|
||||||
#----------------------------------------------------------------
|
#-----------------------------------------------------------------------
|
||||||
# Define the "lvs" command as a way of calling the netgen options
|
# Define the "lvs" command as a way of calling the netgen options
|
||||||
# for standard compare, essentially the same as the old "netcomp"
|
# for standard compare, essentially the same as the old "netcomp"
|
||||||
# standalone program.
|
# 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
|
# although if the cells have not been read in yet, then the
|
||||||
# original syntax of filename or {filename cellname} is required.
|
# original syntax of filename or {filename cellname} is required.
|
||||||
#
|
#
|
||||||
# "args" is passed to verify and may therefore contain only the
|
# "args" may be "-list", "-json", or "-blackbox".
|
||||||
# value "-list" or nothing. If "-list", then output is returned
|
# "-list" returns output as a nested list.
|
||||||
# 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} {
|
proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||||
set dolist 0
|
set dolist 0
|
||||||
set dojson 0
|
set dojson 0
|
||||||
|
set noflat {}
|
||||||
foreach arg $args {
|
foreach arg $args {
|
||||||
if {$arg == "-list"} {
|
if {$arg == "-list"} {
|
||||||
puts stdout "Generating list result"
|
puts stdout "Generating list result"
|
||||||
@@ -382,9 +388,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
|||||||
} elseif {$arg == "-blackbox"} {
|
} elseif {$arg == "-blackbox"} {
|
||||||
puts stdout "Treating empty subcircuits as black-box cells"
|
puts stdout "Treating empty subcircuits as black-box cells"
|
||||||
netgen::model blackbox on
|
netgen::model blackbox on
|
||||||
} elseif {$arg == "-full"} {
|
} elseif {[string first "-noflatten=" $arg] == 0} {
|
||||||
puts stdout "Using full symmetry breaking method"
|
set value [string range $arg 11 end]
|
||||||
netgen::symmetry full
|
# 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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,6 +531,7 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
set properr {}
|
set properr {}
|
||||||
|
set matcherr {}
|
||||||
set pinsgood 0
|
set pinsgood 0
|
||||||
while {$endval != {}} {
|
while {$endval != {}} {
|
||||||
if {$dolist == 1} {
|
if {$dolist == 1} {
|
||||||
@@ -532,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)
|
# Flatten the non-matching subcircuit (but not the top-level cells)
|
||||||
if {[netgen::print queue] != {}} {
|
if {[netgen::print queue] != {}} {
|
||||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
if {([lsearch $noflat [lindex $endval 0]] == -1) &&
|
||||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
([lsearch $noflat [lindex $endval 1]] == -1)} {
|
||||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
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 {
|
} else {
|
||||||
# Match pins
|
# Match pins
|
||||||
@@ -557,9 +606,32 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
|||||||
} else {
|
} else {
|
||||||
# Flatten the non-matching subcircuit (but not the top-level cells)
|
# Flatten the non-matching subcircuit (but not the top-level cells)
|
||||||
if {[netgen::print queue] != {}} {
|
if {[netgen::print queue] != {}} {
|
||||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
if {([lsearch $noflat [lindex $endval 1]] == -1) &&
|
||||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
([lsearch $noflat [lindex $endval 1]] == -1)} {
|
||||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
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
|
netgen::log echo off
|
||||||
@@ -582,6 +654,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
|||||||
if {$properr != {}} {
|
if {$properr != {}} {
|
||||||
netgen::log put "The following cells had property errors: $properr\n"
|
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} {
|
if {$dolog} {
|
||||||
netgen::log end
|
netgen::log end
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-41
@@ -246,8 +246,7 @@ Command netcmp_cmds[] = {
|
|||||||
"\n "
|
"\n "
|
||||||
"toggle exhaustive subdivision"},
|
"toggle exhaustive subdivision"},
|
||||||
{"symmetry", _netcmp_symmetry,
|
{"symmetry", _netcmp_symmetry,
|
||||||
"[fast|full]\n "
|
"(deprecated)"},
|
||||||
"apply method for symmetry breaking"},
|
|
||||||
{"restart", _netcmp_restart,
|
{"restart", _netcmp_restart,
|
||||||
"\n "
|
"\n "
|
||||||
"start over (reset data structures)"},
|
"start over (reset data structures)"},
|
||||||
@@ -2458,8 +2457,7 @@ _netcmp_run(ClientData clientData,
|
|||||||
automorphisms = ResolveAutomorphsByProperty();
|
automorphisms = ResolveAutomorphsByProperty();
|
||||||
if (automorphisms == 0)
|
if (automorphisms == 0)
|
||||||
Fprintf(stdout, "Netlists match uniquely.\n");
|
Fprintf(stdout, "Netlists match uniquely.\n");
|
||||||
else {
|
else if (automorphisms > 0) {
|
||||||
|
|
||||||
// Next, attempt to resolve automorphisms uniquely by
|
// Next, attempt to resolve automorphisms uniquely by
|
||||||
// using the pin names
|
// using the pin names
|
||||||
automorphisms = ResolveAutomorphsByPin();
|
automorphisms = ResolveAutomorphsByPin();
|
||||||
@@ -2467,14 +2465,17 @@ _netcmp_run(ClientData clientData,
|
|||||||
|
|
||||||
if (automorphisms == 0)
|
if (automorphisms == 0)
|
||||||
Fprintf(stdout, "Netlists match uniquely.\n");
|
Fprintf(stdout, "Netlists match uniquely.\n");
|
||||||
else
|
else if (automorphisms > 0) {
|
||||||
// Anything left is truly indistinguishable
|
// Anything left is truly indistinguishable
|
||||||
Fprintf(stdout, "Netlists match with %d symmetr%s.\n",
|
Fprintf(stdout, "Netlists match with %d symmetr%s.\n",
|
||||||
automorphisms, (automorphisms == 1) ? "y" : "ies");
|
automorphisms, (automorphisms == 1) ? "y" : "ies");
|
||||||
|
|
||||||
while ((automorphisms = ResolveAutomorphisms()) > 0);
|
while ((automorphisms = ResolveAutomorphisms()) > 0);
|
||||||
if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n");
|
}
|
||||||
else Fprintf(stdout, "Circuits match correctly.\n");
|
if (automorphisms == -1)
|
||||||
|
Fprintf(stdout, "Netlists do not match.\n");
|
||||||
|
else
|
||||||
|
Fprintf(stdout, "Circuits match correctly.\n");
|
||||||
}
|
}
|
||||||
if (PropertyErrorDetected) {
|
if (PropertyErrorDetected) {
|
||||||
Fprintf(stdout, "There were property errors.\n");
|
Fprintf(stdout, "There were property errors.\n");
|
||||||
@@ -2828,11 +2829,13 @@ _netcmp_equate(ClientData clientData,
|
|||||||
char *name1 = NULL, *name2 = NULL, *optstart;
|
char *name1 = NULL, *name2 = NULL, *optstart;
|
||||||
struct nlist *tp1, *tp2, *SaveC1, *SaveC2;
|
struct nlist *tp1, *tp2, *SaveC1, *SaveC2;
|
||||||
struct objlist *ob1, *ob2;
|
struct objlist *ob1, *ob2;
|
||||||
|
struct ElementClass *saveEclass = NULL;
|
||||||
|
struct NodeClass *saveNclass = NULL;
|
||||||
int file1, file2;
|
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;
|
Tcl_Obj *tobj1, *tobj2, *tobj3;
|
||||||
|
|
||||||
if (objc > 1) {
|
while (objc > 1) {
|
||||||
optstart = Tcl_GetString(objv[1]);
|
optstart = Tcl_GetString(objv[1]);
|
||||||
if (*optstart == '-') optstart++;
|
if (*optstart == '-') optstart++;
|
||||||
if (!strcmp(optstart, "list")) {
|
if (!strcmp(optstart, "list")) {
|
||||||
@@ -2840,6 +2843,13 @@ _netcmp_equate(ClientData clientData,
|
|||||||
objv++;
|
objv++;
|
||||||
objc--;
|
objc--;
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(optstart, "force")) {
|
||||||
|
doforce = 1;
|
||||||
|
objv++;
|
||||||
|
objc--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((objc != 2) && (objc != 4) && (objc != 6)) {
|
if ((objc != 2) && (objc != 4) && (objc != 6)) {
|
||||||
@@ -2997,6 +3007,12 @@ _netcmp_equate(ClientData clientData,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PINS_IDX:
|
case PINS_IDX:
|
||||||
|
if ((ElementClasses != NULL) && (doforce == TRUE)) {
|
||||||
|
saveEclass = ElementClasses;
|
||||||
|
saveNclass = NodeClasses;
|
||||||
|
ElementClasses = NULL;
|
||||||
|
NodeClasses = NULL;
|
||||||
|
}
|
||||||
if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) {
|
if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) {
|
||||||
if (CurrentCell == NULL) {
|
if (CurrentCell == NULL) {
|
||||||
Fprintf(stderr, "Equate elements: no current cell.\n");
|
Fprintf(stderr, "Equate elements: no current cell.\n");
|
||||||
@@ -3059,6 +3075,12 @@ _netcmp_equate(ClientData clientData,
|
|||||||
/* Recover temporarily set global variables (see above) */
|
/* Recover temporarily set global variables (see above) */
|
||||||
Circuit1 = SaveC1;
|
Circuit1 = SaveC1;
|
||||||
Circuit2 = SaveC2;
|
Circuit2 = SaveC2;
|
||||||
|
|
||||||
|
/* Recover ElementClasses if forcing pins on mismatched circuits */
|
||||||
|
if (doforce == TRUE) {
|
||||||
|
ElementClasses = saveEclass;
|
||||||
|
NodeClasses = saveNclass;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -3321,10 +3343,10 @@ _netcmp_property(ClientData clientData,
|
|||||||
int result, index, idx2;
|
int result, index, idx2;
|
||||||
|
|
||||||
char *suboptions[] = {
|
char *suboptions[] = {
|
||||||
"integer", "double", "value", "string", NULL
|
"integer", "double", "value", "string", "expression", NULL
|
||||||
};
|
};
|
||||||
enum SubOptionIdx {
|
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. */
|
/* Note: "merge" has been deprecated, but kept for backwards compatibility. */
|
||||||
@@ -3418,9 +3440,15 @@ _netcmp_property(ClientData clientData,
|
|||||||
GlobalParallelNone = FALSE;
|
GlobalParallelNone = FALSE;
|
||||||
SetParallelCombine(TRUE);
|
SetParallelCombine(TRUE);
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(Tcl_GetString(objv[2]), "connected")) {
|
||||||
|
GlobalParallelOpen = FALSE;
|
||||||
|
}
|
||||||
|
else if (!strcmp(Tcl_GetString(objv[2]), "open")) {
|
||||||
|
GlobalParallelOpen = TRUE;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
Tcl_SetResult(interp, "Bad option, should be property parallel none|all",
|
Tcl_SetResult(interp, "Bad option, should be property parallel "
|
||||||
NULL);
|
"none|all|connected", NULL);
|
||||||
return TCL_ERROR;
|
return TCL_ERROR;
|
||||||
}
|
}
|
||||||
return TCL_OK;
|
return TCL_OK;
|
||||||
@@ -3707,6 +3735,11 @@ _netcmp_property(ClientData clientData,
|
|||||||
PropertyString(tp->name, fnum,
|
PropertyString(tp->name, fnum,
|
||||||
Tcl_GetString(tobj1), ival, NULL);
|
Tcl_GetString(tobj1), ival, NULL);
|
||||||
break;
|
break;
|
||||||
|
case EXPRESSION_IDX:
|
||||||
|
PropertyString(tp->name, fnum,
|
||||||
|
Tcl_GetString(tobj1), 0,
|
||||||
|
Tcl_GetString(tobj3));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3982,39 +4015,14 @@ _netcmp_permute(ClientData clientData,
|
|||||||
/* Formerly: x */
|
/* Formerly: x */
|
||||||
/* Results: */
|
/* Results: */
|
||||||
/* Side Effects: */
|
/* Side Effects: */
|
||||||
|
/* Notes: Deprecated, retained for compatibility. */
|
||||||
/*------------------------------------------------------*/
|
/*------------------------------------------------------*/
|
||||||
|
|
||||||
int
|
int
|
||||||
_netcmp_symmetry(ClientData clientData,
|
_netcmp_symmetry(ClientData clientData,
|
||||||
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
|
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
|
||||||
{
|
{
|
||||||
char *fastfull[] = {
|
Printf("Symmetry breaking method has been deprecated.\n");
|
||||||
"fast", "full", NULL
|
|
||||||
};
|
|
||||||
enum OptionIdx {
|
|
||||||
FAST_IDX, FULL_IDX
|
|
||||||
};
|
|
||||||
int result, index;
|
|
||||||
|
|
||||||
if (objc == 1)
|
|
||||||
index = -1;
|
|
||||||
else {
|
|
||||||
if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)fastfull,
|
|
||||||
"option", 0, &index) != TCL_OK)
|
|
||||||
return TCL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(index) {
|
|
||||||
case FAST_IDX:
|
|
||||||
FastSymmetryBreaking = TRUE;
|
|
||||||
break;
|
|
||||||
case FULL_IDX:
|
|
||||||
FastSymmetryBreaking = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Printf("Symmetry breaking method: %s.\n",
|
|
||||||
FastSymmetryBreaking ? "FAST" : "FULL");
|
|
||||||
|
|
||||||
return TCL_OK;
|
return TCL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user