Compare commits

...
20 Commits
Author SHA1 Message Date
Tim Edwards 626faf22f9 Merge branch 'master' into netgen-1.5 2021-06-17 03:00:10 -04:00
Tim Edwards 1c5457e180 Corrected a minor issue that cropped up today in which the search
for file extensions is greedy and picks the first matching extension
starting at the front of the string, such that, e.g., "file.ext.spice"
is interpreted as a ".ext" file and not a ".spice" file.
2021-06-16 15:20:39 -04:00
Tim Edwards 7d246c36a6 Corrected an issue with flattening when the instances to be flattened
run to the end of the list of circuit elements.  Also corrected
another issue caused by the flag to denote multiple no-connect pins,
which can be on an instance pin and so cannot share the data from the
instance record.
2021-06-16 14:32:14 -04:00
Tim Edwards 550234dce5 Merge branch 'master' into netgen-1.5 2021-06-15 03:00:36 -04:00
Tim Edwards 92dfa74403 Corrected an issue that was caused by introducing a type of parallel
device with one or more no-connect pins.  The flag that indicates a
no-connect pin was checked incorrectly, potentially causing obscure
and misleading property mismatch messages to be generated.
2021-06-14 14:30:10 -04:00
Tim Edwards a4c0028706 Merge branch 'master' into netgen-1.5 2021-06-13 03:00:32 -04:00
Tim Edwards d53541d1d3 Correction to previous commit (failed to link to last pointer
after processing properties during flattening).
2021-06-12 20:15:55 -04:00
Tim Edwards 57b2d21221 Modified the way flattening is done to account for multiple property
records, which were being ignored.  This really only applies to
parallel subcircuits being flattened.  To flatten correctly requires
that any circuit with N property records must be flattened into the
parent at least N times.  To do:  Must look for M > 1 records in the
properties and flatten (M - 1) additional times.
2021-06-12 17:37:50 -04:00
Tim Edwards e7ae84bbc8 Merge branch 'master' into netgen-1.5 2021-06-10 03:00:09 -04:00
Tim Edwards dfabdaa367 Modified the JSON output so that missing pins which do not match
unconnected pins in the other netlist are marked as "(no pin)"
rather than "(no matching pin)".  This allows a script parsint the
JSON file (e.g., count_lvs.py from qflow) to distinguish between a
real pin error and one that can be ignored.
2021-06-09 09:34:02 -04:00
Tim Edwards c5c1306f6a Corrected a minor error where the property mismatch check outputs
an error message while doing a non-final iteration, where all
output is supposed to be suppressed.
2021-06-09 09:20:45 -04:00
Tim Edwards 43c018f3bc Merge branch 'master' into netgen-1.5 2021-06-09 03:00:10 -04:00
Tim Edwards 0614461c84 Modified MatchPins so that pin mismatches on nets that are not
connected to anything are once again ignored (the prior commit
to prevent netgen from not reporting swapped pins as an error
overcorrected).
2021-06-08 17:25:17 -04:00
Tim Edwards 6e94886b65 Messed up basic pin comparison between black-box vs. non-black-box
subcircuit situations in the last commit.  This commit fixes it.
2021-06-08 16:52:57 -04:00
Tim Edwards ca119edc0a Merge branch 'master' into netgen-1.5 2021-06-04 03:00:10 -04:00
Tim Edwards 0556abf3c1 Finally corrected the issue with pins being able to be swapped
without netgen noticing---this behavior got broken by an
exception for "black box" circuits, but failed to check if the
circuit really was marked as a "black box" or not.  Fixing that
revealed another issue with verilog implicit pins.  Both issues
have now been corrected.
2021-06-03 17:42:54 -04:00
Tim Edwards 84348d2fe1 Merge branch 'master' into netgen-1.5 2021-05-31 03:00:10 -04:00
Tim Edwards 3356dd784e Made a number of usability corrections to the python GUI for
netgen.  More work will be done later.  Also:  Removed the
derived file lvs_manager.py from the repository, and modified
the Makefile to remove it as part of "make clean".
2021-05-30 20:39:47 -04:00
Tim Edwards 813b29cdc4 Merge branch 'master' into netgen-1.5 2021-05-29 03:00:10 -04:00
Tim Edwards 00949970b4 The previous method change for sorting to accommodate capacitors and
other devices that do not have a "critical property" that determines
which properties can add together was flawed and messed up the sorting
of devices like transistors and resistors that do have a critical
property.  Reworked the sorting order so that it makes sense for both
situations.
2021-05-28 21:20:39 -04:00
11 changed files with 617 additions and 1194 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.180
1.5.188
+238 -191
View File
@@ -254,9 +254,10 @@ void flattenCell(char *name, int file)
int flattenInstancesOf(char *name, int fnum, char *instance)
{
struct objlist *ParentParams;
struct objlist *ParentProps;
struct objlist *ParentProps, *CurrentProp;
struct objlist *NextObj, *LastObj, *prepp;
struct objlist *ChildObjList;
struct objlist *ChildObjList, *ChildListEnd;
struct objlist *ChildStart, *ChildEnd, *ParentEnd, *ParentNext;
struct nlist *ThisCell;
struct nlist *ChildCell;
struct objlist *tmp, *ob2, *ob3;
@@ -334,215 +335,261 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
ParentProps = ParentProps->next) {
if (ParentProps->type == PROPERTY) break;
}
if (ParentProps && (ParentProps->type != PROPERTY)) ParentProps = NULL;
/* not primitive, so need to flatten this instance */
notdone = 1;
/* if this is a new instance, flatten it */
/* if (ChildCell->dumped == 0) flattenCell(ParentParams->model.class, file); */
ChildObjList = CopyObjList(ChildCell->cell, 1);
numflat++;
/* update node numbers in child to unique numbers */
oldmax = 0;
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
if (tmp->node > oldmax) oldmax = tmp->node;
if (nextnode <= oldmax) nextnode = oldmax + 1;
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
if (tmp->node <= oldmax && tmp->node > 0) {
if (Debug) Printf("Update node %d --> %d\n", tmp->node, nextnode);
UpdateNodeNumbers(ChildObjList, tmp->node, nextnode);
nextnode++;
}
/* copy nodenumbers of ports from parent */
ob2 = ParentParams;
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
if (IsPort(tmp)) {
if (tmp->node > 0) {
if (ob2->node == -1) {
// Before commiting to attaching to a unconnected node, see
// if there is another node in ParentParams with the same
// name and a valid node number. If so, connect them. In
// the broader case, it may be necessary to consider all
// nodes, not just those with node == -1, and call join()
// here to update all node numbers in the parent cell.
// In that case, a more efficient method is needed for
// tracking same-name ports.
for (ob3 = ParentParams; ob3 && ob3->type >= FIRSTPIN; ob3 = ob3->next) {
if (ob3 == ob2) continue;
if ((*matchfunc)(ob3->name, ob2->name) && ob3->node != -1) {
ob2->node = ob3->node;
break;
}
}
}
if (Debug) {
// Printf(" Sealing port: %d to node %d\n", tmp->node, ob2->node);
Printf("Update node %d --> %d\n", tmp->node, ob2->node);
}
UpdateNodeNumbers(ChildObjList, tmp->node, ob2->node);
}
/* in pathological cases, the lengths of the port lists may
* change. This is an error, but that is no reason to allow
* the code to core dump. We avoid this by placing a
* superfluous check on ob2->type
*/
if (ob2 != NULL)
ob2 = ob2->next;
if (ob2 == NULL) break;
}
/* Using name == NULL to indicate that a .ext file is being */
/* flattened on the fly. This is quick & dirty. */
if (name != NULL) {
/* delete all port elements from child */
while ((ChildObjList != NULL) && IsPort(ChildObjList)) {
/* delete all ports at beginning of list */
if (Debug) Printf("deleting leading port from child\n");
tmp = ChildObjList->next;
// FreeObjectAndHash(ChildObjList, ChildCell);
FreeObject(ChildObjList);
ChildObjList = tmp;
}
tmp = ChildObjList;
while (tmp && (tmp->next != NULL)) {
if (IsPort(tmp->next)) {
ob2 = (tmp->next)->next;
if (Debug) Printf("deleting a port from child\n");
// FreeObjectAndHash(tmp->next, ChildCell);
FreeObject(tmp->next);
tmp->next = ob2;
}
else tmp = tmp->next;
}
if (ParentProps && (ParentProps->type != PROPERTY)) {
ParentProps = NULL;
CurrentProp = NULL;
}
else
CurrentProp = ParentProps;
/* for each element in child, prepend 'prefix' */
/* Find the end record of the parent cell and save it */
for (ParentEnd = (ParentProps) ? ParentProps : ParentParams;
ParentEnd && ParentEnd->next && 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;
}
if (ChildStart == NULL) {
if (ChildListEnd == ChildEnd) ChildListEnd = NULL;
ChildEnd = NULL;
}
}
/* for each element in child, prepend 'prefix' */
#if !OLDPREFIX
/* replaces all the sprintf's below */
strcpy(tmpstr,ParentParams->instance.name);
strcat(tmpstr,SEPARATOR);
prefixlength = strlen(tmpstr);
/* replaces all the sprintf's below */
strcpy(tmpstr,ParentParams->instance.name);
strcat(tmpstr,SEPARATOR);
prefixlength = strlen(tmpstr);
#endif
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) {
if (tmp->type == PROPERTY)
continue;
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) {
if (tmp->type == PROPERTY)
continue;
else if (IsGlobal(tmp)) {
/* Keep the name but search for node of same name in parent */
/* and replace the node number, if found. */
else if (IsGlobal(tmp)) {
/* Keep the name but search for node of same name in parent */
/* and replace the node number, if found. */
for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) {
/* Type in parent may be a port, not a global */
if (ob2->type == tmp->type || ob2->type == PORT) {
if ((*matchfunc)(tmp->name, ob2->name)) {
if (ob2->node >= 0) {
// Replace all child objects with this node number
rnodenum = tmp->node;
for (ob3 = ChildObjList; ob3 != NULL; ob3 = ob3->next) {
if (ob3->node == rnodenum)
ob3->node = ob2->node;
}
break;
}
}
}
}
// Don't hash this if the parent had a port of this name
if (!ob2 || ob2->type != PORT)
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
continue;
}
for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) {
/* Type in parent may be a port, not a global */
if (ob2->type == tmp->type || ob2->type == PORT) {
if ((*matchfunc)(tmp->name, ob2->name)) {
if (ob2->node >= 0) {
// Replace all child objects with this node number
rnodenum = tmp->node;
for (ob3 = ChildStart; ob3 != NULL; ob3 = ob3->next) {
if (ob3->node == rnodenum)
ob3->node = ob2->node;
}
break;
}
}
}
}
// Don't hash this if the parent had a port of this name
if (!ob2 || ob2->type != PORT)
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
continue;
}
#if OLDPREFIX
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->name);
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->name);
#else
strcpy(tmpstr+prefixlength,tmp->name);
strcpy(tmpstr+prefixlength,tmp->name);
#endif
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
FreeString(tmp->name);
tmp->name = strsave(tmpstr);
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
FreeString(tmp->name);
tmp->name = strsave(tmpstr);
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
#if OLDPREFIX
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->instance.name);
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->instance.name);
#else
strcpy(tmpstr+prefixlength,tmp->instance.name);
strcpy(tmpstr+prefixlength,tmp->instance.name);
#endif
FreeString(tmp->instance.name);
tmp->instance.name = strsave(tmpstr);
if (tmp->type == FIRSTPIN)
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
}
}
/* Do property inheritance */
if (ParentProps) {
for (ob2 = ChildObjList; ob2 != NULL; ob2=ob2->next) {
/* If the parent cell has properties to declare, then */
/* pass them on to children. Use globals only if the */
/* spiceparams dictionary is active (during file reading */
/* only). */
if (ob2->type == PROPERTY)
ReduceExpressions(ob2, ParentProps, ChildCell,
(spiceparams.hashtab == NULL) ? 0 : 1);
FreeString(tmp->instance.name);
tmp->instance.name = strsave(tmpstr);
if (tmp->type == FIRSTPIN)
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
}
}
/* Do property inheritance */
/* NOTE: Need to do: Check properties for M > 1 and decrement
* and repeat without moving CurrentProp
*/
if (CurrentProp) {
for (ob2 = ChildStart; ob2 != NULL; ob2=ob2->next) {
/* If the parent cell has properties to declare, then */
/* pass them on to children. Use globals only if the */
/* spiceparams dictionary is active (during file */
/* reading only). */
if (ob2->type == PROPERTY)
ReduceExpressions(ob2, CurrentProp, ChildCell,
(spiceparams.hashtab == NULL) ? 0 : 1);
}
/* Repeat for each property record, as each property represents a
* unique instance that must be flattened individually.
*/
CurrentProp = CurrentProp->next;
if (CurrentProp->type != PROPERTY) break;
}
else break;
/* Put the child cell at the start of ChildObjList */
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
}
/* splice instance out of parent */
if ((ParentParams == ThisCell->cell) && (ChildObjList == NULL)) {
ThisCell->cell = ob2; /* Child cell was empty */
tmp = ob2;
/* Put the child cell at the start of ChildObjList */
if (ChildEnd) {
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
}
else {
/* Pull the instance out of the parent */
if ((ParentParams != ThisCell->cell) || (ChildObjList != NULL)) {
if (ParentParams == ThisCell->cell) {
/* ParentParams are the very first thing in the list */
ThisCell->cell = ChildObjList;
for (ob2 = ChildObjList; ob2 && ob2->next != NULL; ob2 = ob2->next) ;
/* ParentParams are the very first thing in the list */
ThisCell->cell = ChildObjList;
}
else {
/* find ParentParams in ThisCell list. In most cases, LastObj */
/* should be pointing to it. */
if (LastObj && (LastObj->next == ParentParams)) {
ob2 = LastObj;
}
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);
}
}
if (ob2)
for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ;
else if (ChildObjList) {
/* find ParentParams in ThisCell list. In most cases, LastObj */
/* should be pointing to it. */
if (LastObj && (LastObj->next == ParentParams)) {
LastObj->next = ChildObjList;
}
else {
for (ob2 = LastObj; ob2 && ob2->next != ParentParams;
ob2 = ob2->next);
if (ob2 == NULL) {
/* It should not happen that LastObj is ahead of ParentParams */
/* but just in case, this long loop will find it. */
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams;
ob2 = ob2->next);
}
ob2->next = ChildObjList;
}
}
/* now, ob2 is last element in child list, so skip and reclaim parent */
else {
/* The child was completely optimized out, so close list around it */
LastObj->next = ParentEnd->next;
}
tmp = ParentParams;
do {
tmp = tmp->next;
} while ((tmp != NULL) && ((tmp->type > FIRSTPIN) || (tmp->type == PROPERTY)));
if (ob2) ob2->next = tmp;
/* Link end of child list into the parent */
if (ChildListEnd && ParentEnd)
ChildListEnd->next = ParentEnd->next;
}
while (ParentParams != tmp) {
ob2 = ParentParams->next;
FreeObjectAndHash(ParentParams, ThisCell);
ParentParams = ob2;
ParentNext = (ParentEnd) ? ParentEnd->next : NULL;
while (ParentParams != ParentNext) {
ob2 = ParentParams->next;
FreeObjectAndHash(ParentParams, ThisCell);
ParentParams = ob2;
}
NextObj = ParentParams;
} /* repeat until no more instances found */
@@ -1538,13 +1585,13 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
}
}
if (match) {
if (ecomp->cell1) {
if (ecomp->cell1 && (ecomp->num1 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s"
" makes a better match\n", ecomp->cell1->name,
name1);
flattenInstancesOf(name1, file1, ecomp->cell1->name);
}
if (ecomp->cell2) {
if (ecomp->cell2 && (ecomp->num2 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s"
" makes a better match\n", ecomp->cell2->name,
name2);
+167 -137
View File
@@ -4254,10 +4254,12 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
struct objlist *obn, *obp;
propsort *proplist;
struct property *kl;
struct valuelist *vl, *ml;
int i, p, mval, merge_type, has_crit = FALSE;
char c, *subs_crit = NULL;
double cval, tval, aval, slop, tslop;
struct valuelist *vl;
int i, p, mval, merge_type;
int has_crit;
char ca, co;
double tval, tslop;
double aval, pval, oval, aslop, pslop;
obn = ob1->next;
for (i = 0; i < idx1; i++) obn = obn->next;
@@ -4266,96 +4268,99 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
* value and index. Then sort that list, then use the sorted
* indexes to sort the actual property linked list.
*
* If there is no critical property listed, then it will sort on
* M first, and any additive property second, or any property at
* all if no additive property was found. It is doubtful that any
* use case requires more than two properties for sorting.
* Collect properties of interest that we can potentially sort on.
* That includes the M value, critical parameter, additive value(s),
* and finally any parameter. Keep these values and their slop.
* If there is a critical value, then sort on additive values * M.
* If not, then sort on M and additive values.
*/
proplist = (propsort *)MALLOC(run * sizeof(propsort));
obp = obn;
mval = 1;
cval = aval = slop = 0.0;
pval = aval = oval = 0.0;
for (i = 0; i < run; i++) {
has_crit = FALSE;
merge_type = MERGE_NONE;
ml = NULL;
c = (char)0;
ca = co = (char)0;
for (p = 0;; p++) {
vl = &(obp->instance.props[p]);
if (vl->type == PROP_ENDLIST) break;
if (vl->key == NULL) continue;
if ((*matchfunc)(vl->key, "M")) {
mval = vl->value.ival;
ml = vl;
continue;
}
kl = (struct property *)HashLookup(vl->key, &(tp1->propdict));
if (kl == NULL) continue; /* Ignored property */
if (kl->merge & MERGE_P_CRIT)
has_crit = TRUE;
else if (kl->merge & (MERGE_P_ADD | MERGE_P_PAR)) {
if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) &&
(kl->type != vl->type))
PromoteProperty(kl, vl, obp, tp1);
if (vl->type == PROP_INTEGER) {
tval = (double)vl->value.ival;
tslop = (double)kl->slop.ival;
}
else if (vl->type == PROP_STRING) {
/* This is unlikely---no method to merge string properties! */
tval = (double)vl->value.string[0]
+ (double)vl->value.string[1] / 10.0;
tslop = (double)0;
}
else {
tval = vl->value.dval;
tslop = kl->slop.dval;
}
if (merge_type == MERGE_NONE)
{
merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
cval = tval;
slop = tslop;
}
else {
if ((c == (char)0) || (toupper(vl->key[0]) > c)) {
c = toupper(vl->key[0]);
aval = tval;
}
}
/* Get the property value and slop. Promote if needed. Save */
/* property and slop as type double so they can be sorted. */
if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) &&
(kl->type != vl->type))
PromoteProperty(kl, vl, obp, tp1);
if (vl->type == PROP_INTEGER) {
tval = (double)vl->value.ival;
tslop = (double)kl->slop.ival;
}
else if (vl->type == PROP_STRING) {
/* This is unlikely---no method to merge string properties! */
tval = (double)vl->value.string[0]
+ (double)vl->value.string[1] / 10.0;
tslop = (double)0;
}
else {
if ((c == (char)0) || (toupper(vl->key[0]) > c)) {
if (vl->type == PROP_INTEGER) {
aval = (double)vl->value.ival;
c = toupper(vl->key[0]);
}
else if (vl->type == PROP_DOUBLE) {
aval = vl->value.dval;
c = toupper(vl->key[0]);
}
tval = vl->value.dval;
tslop = kl->slop.dval;
}
if (kl->merge & MERGE_P_CRIT) {
has_crit = TRUE;
pval = tval;
pslop = tslop;
}
else if (kl->merge & (MERGE_P_ADD | MERGE_P_PAR)) {
if ((ca == (char)0) || (toupper(vl->key[0]) > ca)) {
merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
aval = tval;
aslop = tslop;
ca = toupper(vl->key[0]);
}
}
}
else if ((co == (char)0) || (toupper(vl->key[0]) > co)) {
oval = tval;
co = toupper(vl->key[0]);
}
}
if (merge_type == MERGE_P_ADD) {
proplist[i].value = cval * (double)mval;
proplist[i].avalue = aval;
proplist[i].slop = tslop;
if (ml) ml->value.ival = 1;
}
else if (merge_type == MERGE_P_PAR) {
proplist[i].value = cval / (double)mval;
proplist[i].avalue = aval;
proplist[i].slop = tslop;
if (ml) ml->value.ival = 1;
if (has_crit == TRUE) {
/* If there is a critical value, then sort first */
/* by critical value */
proplist[i].value = pval;
proplist[i].slop = pslop;
/* then sort on additive value times M */
/* or on non-additive value. */
if (merge_type == MERGE_P_ADD)
proplist[i].avalue = aval * (double)mval;
else if (merge_type == MERGE_P_PAR)
proplist[i].avalue = aval / (double)mval;
else
proplist[i].avalue = (double)mval;
}
else {
/* If there are no additive values, then sort first by M */
/* and second by aval */
proplist[i].value = (double)mval;
proplist[i].avalue = aval;
proplist[i].slop = (double)0;
if (merge_type != MERGE_NONE) {
proplist[i].value = aval;
proplist[i].slop = aslop;
proplist[i].avalue = (double)mval;
}
else {
proplist[i].value = (double)mval;
proplist[i].slop = (double)0;
proplist[i].avalue = oval;
}
}
proplist[i].idx = i;
proplist[i].ob = obp;
@@ -5686,13 +5691,12 @@ PropertyMatch(struct objlist *ob1, int file1,
#endif
}
/* WIP---Check for no-connect pins in merged devices on both sides. */
/* Check for no-connect pins in merged devices on both sides. */
/* Both sides should either have no-connects marked, or neither. */
/* (Permutable pins may need to be handled correctly. . . */
for (tp1 = ob1, tp2 = ob2; (tp1 != NULL) && tp1->type >= FIRSTPIN &&
(tp2 != NULL) && tp2->type >= FIRSTPIN; tp1 = tp1->next, tp2 = tp2->next)
{
(tp2 != NULL) && tp2->type >= FIRSTPIN; tp1 = tp1->next, tp2 = tp2->next) {
struct objlist *node1, *node2;
if (file1 == Circuit1->file)
@@ -5705,17 +5709,20 @@ PropertyMatch(struct objlist *ob1, int file1,
else
node2 = Circuit2->nodename_cache[tp2->node];
if (node1->instance.flags != node2->instance.flags)
{
/* NOTE: A "no-connect" node (multiple no-connects represented by a
* single node) has non-zero flags. A non-node entry in the cache
* implies a node with zero flags.
*/
if (node1->flags != node1->flags) {
Fprintf(stdout, " Parallelized instances disagree on pin connections.\n");
Fprintf(stdout, " Circuit1 instance %s pin %s connections are %s (%d)\n",
tp1->instance.name, node1->name,
(node1->instance.flags == 0) ? "tied together" : "no connects",
node1->instance.flags);
(node1->flags == 0) ? "tied together" : "no connects",
node1->flags);
Fprintf(stdout, " Circuit2 instance %s pin %s connections are %s (%d)\n",
tp2->instance.name, node2->name,
(node2->instance.flags == 0) ? "tied together" : "no connects",
node2->instance.flags);
(node2->flags == 0) ? "tied together" : "no connects",
node2->flags);
mismatches++;
}
}
@@ -5741,10 +5748,12 @@ PropertyMatch(struct objlist *ob1, int file1,
if ((t1type != PROPERTY) && (checked_one == TRUE)) {
// t2 has more property records than t1, and they did not get
// merged equally by PropertySortAndCombine().
Fprintf(stdout, "Circuit 1 parallel/series network does not match"
if (do_print) {
Fprintf(stdout, "Circuit 1 parallel/series network does not match"
" Circuit 2\n");
DumpNetwork(ob1, 1);
DumpNetwork(ob2, 2);
DumpNetwork(ob1, 1);
DumpNetwork(ob2, 2);
}
mismatches++;
}
else if (t1type != PROPERTY) {
@@ -6799,6 +6808,11 @@ int reorderpins(struct hashlist *p, int file)
"Ordering will be arbitrary.\n", tc2->name);
for (ob = ptr->cell; ob != NULL; ) {
/* Catch badness */
if (ob->next && (ob->next->node > 100000)) {
Fprintf(stdout, "Bad.\n");
}
if (ob->type == FIRSTPIN) {
if ((*matchfunc)(ob->model.class, tc2->name)) {
char *sptr = ob->instance.name;
@@ -6817,6 +6831,7 @@ int reorderpins(struct hashlist *p, int file)
nodes[ob2->model.port] = ob->node;
names[ob2->model.port] = ob->name;
}
ob = ob->next;
ob2 = ob2->next;
if (i < numports - 1) {
@@ -7235,71 +7250,75 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
}
}
/* Do any unmatched pins have the same name? */
/* (This should not happen if unconnected pins are eliminated) */
/* Do any unmatched pins have the same name? */
/* This should not happen if unconnected pins are eliminated */
/* so apply only to black-box (CELL_PLACEHOLDER) entries. */
/* (Semi-hack: Allow "!" global flag) */
ob1 = tc1->cell;
bangptr1 = strrchr(ob1->name, '!');
if (bangptr1 && (*(bangptr1 + 1) == '\0'))
*bangptr1 = '\0';
else bangptr1 = NULL;
if (((tc1->flags & CELL_PLACEHOLDER) && (tc2->flags & CELL_PLACEHOLDER)) ||
(NodeClasses == NULL)) {
ob1 = tc1->cell;
bangptr1 = strrchr(ob1->name, '!');
if (bangptr1 && (*(bangptr1 + 1) == '\0'))
*bangptr1 = '\0';
else bangptr1 = NULL;
for (i = 0; i < numorig; i++) {
if (*(cover + i) == (char)0) {
j = 0;
for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next) {
char *name1, *name2;
for (i = 0; i < numorig; i++) {
if (*(cover + i) == (char)0) {
j = 0;
for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next) {
char *name1, *name2;
if (!IsPort(ob2)) break;
if (!IsPort(ob2)) break;
bangptr2 = strrchr(ob2->name, '!');
if (bangptr2 && (*(bangptr2 + 1) == '\0'))
*bangptr2 = '\0';
else bangptr2 = NULL;
bangptr2 = strrchr(ob2->name, '!');
if (bangptr2 && (*(bangptr2 + 1) == '\0'))
*bangptr2 = '\0';
else bangptr2 = NULL;
name1 = ob1->name;
name2 = ob2->name;
name1 = ob1->name;
name2 = ob2->name;
/* Recognize proxy pins as matching */
if (!strncmp(name1, "proxy", 5)) name1 +=5;
if (!strncmp(name2, "proxy", 5)) name2 +=5;
/* Recognize proxy pins as matching */
if (!strncmp(name1, "proxy", 5)) name1 +=5;
if (!strncmp(name2, "proxy", 5)) name2 +=5;
if ((*matchfunc)(name1, name2)) {
ob2->model.port = i; /* save order */
*(cover + i) = (char)1;
if ((*matchfunc)(name1, name2)) {
ob2->model.port = i; /* save order */
*(cover + i) = (char)1;
if (Debug == 0) {
for (m = 0; m < left_col_end; m++) *(ostr + m) = ' ';
for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' ';
snprintf(ostr, left_col_end, "%s", ob1->name);
snprintf(ostr + left_col_end + 1, left_col_end, "%s", ob2->name);
for (m = 0; m < right_col_end + 1; m++)
if (*(ostr + m) == '\0') *(ostr + m) = ' ';
Fprintf(stdout, ostr);
}
else {
Fprintf(stdout, "Circuit %s port %d \"%s\""
if (Debug == 0) {
for (m = 0; m < left_col_end; m++) *(ostr + m) = ' ';
for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' ';
snprintf(ostr, left_col_end, "%s", ob1->name);
snprintf(ostr + left_col_end + 1, left_col_end, "%s", ob2->name);
for (m = 0; m < right_col_end + 1; m++)
if (*(ostr + m) == '\0') *(ostr + m) = ' ';
Fprintf(stdout, ostr);
}
else {
Fprintf(stdout, "Circuit %s port %d \"%s\""
" = cell %s port %d \"%s\"\n",
tc1->name, i, ob1->name,
tc2->name, j, ob2->name);
}
}
#ifdef TCL_NETGEN
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
Tcl_NewStringObj(ob1->name, -1));
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj(ob2->name, -1));
}
}
#endif
}
if (bangptr2) *bangptr2 = '!';
j++;
}
if (bangptr2) *bangptr2 = '!';
j++;
}
}
ob1 = ob1->next;
}
ob1 = ob1->next;
if (bangptr1) *bangptr1 = '!';
}
if (bangptr1) *bangptr1 = '!';
/* Find the end of the pin list in tc1, for adding proxy pins */
@@ -7333,15 +7352,6 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
Fprintf(stderr, "No netlist match for cell %s pin %s\n",
tc2->name, ob2->name);
}
#ifdef TCL_NETGEN
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
Tcl_NewStringObj("(no matching pin)", -1));
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj(ob2->name, -1));
}
#endif
result = 0;
/* Before making a proxy pin, check to see if */
/* flattening instances has left a port with a */
@@ -7358,8 +7368,28 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
if ((obt == NULL) && (notempty == 1)) {
ob2->node = -2; // Will run this through cleanuppins
needclean2 = 1;
#ifdef TCL_NETGEN
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
Tcl_NewStringObj("(no pin)", -1));
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj(ob2->name, -1));
}
#endif
continue;
}
else if (notempty == 1) {
/* Flag this as an error */
result = 0;
#ifdef TCL_NETGEN
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
Tcl_NewStringObj("(no matching pin)", -1));
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj(ob2->name, -1));
}
#endif
}
ob2->model.port = numnodes++; // Assign a port order
/* Add a proxy pin to tc1 */
+3 -1
View File
@@ -896,7 +896,9 @@ char *ReadNetlist(char *fname, int *fnum)
/* make first pass looking for extension */
for (index = 0; formats[index].extension != NULL; index++) {
if (strstr(fname, formats[index].extension) != NULL) {
int extlen = strlen(formats[index].extension);
int flen = strlen(fname);
if (!strcmp(fname + flen - extlen, formats[index].extension)) {
return (*(formats[index].proc))(fname, fnum);
}
}
+3 -2
View File
@@ -1352,7 +1352,8 @@ void Node(char *name)
tp->name = strsave(name);
tp->type = NODE; /* internal node type */
tp->model.class = NULL;
tp->instance.flags = 0;
tp->instance.name = NULL;
tp->flags = 0;
tp->node = -1; /* null node */
tp->next = NULL;
AddToCurrentCell (tp);
@@ -3259,7 +3260,7 @@ int CombineParallel(char *model, int file)
if ((ob2->node >= 0) && (nodecount[ob2->node] == 1))
{
nob = (tp->nodename_cache)[ob2->node];
nob->instance.flags = NO_CONNECT;
nob->flags = NO_CONNECT;
strcat(pptr, "_nc");
}
else
+4 -6
View File
@@ -975,10 +975,8 @@ struct objlist *CopyObjList(struct objlist *oldlist, unsigned char doforall)
newob->model.class = NULL;
else
newob->model.class = strsave(tmp->model.class);
if (newob->type == NODE)
newob->instance.flags = tmp->instance.flags;
else
newob->instance.name = (tmp->instance.name) ?
newob->flags = tmp->flags;
newob->instance.name = (tmp->instance.name) ?
strsave(tmp->instance.name) : NULL;
}
newob->node = tmp->node;
@@ -1098,8 +1096,8 @@ void FreeObject(struct objlist *ob)
FREE(ob->instance.props);
}
}
else if (ob->type != NODE) {
/* All other records except NODE, which uses this for flags */
else {
/* All other records */
if (ob->instance.name != NULL) FreeString(ob->instance.name);
}
if (ob->model.class != NULL) FreeString(ob->model.class);
+1 -1
View File
@@ -162,9 +162,9 @@ struct objlist {
union {
char *name; /* unique name for the instance, or */
/* (string) value of property for properties */
int flags; /* Used by NODE type to flag isolated net */
struct valuelist *props; /* Property record */
} instance;
unsigned char flags; /* Used by NODE type to flag isolated net */
int node; /* the electrical node number of the port/node/pin */
struct objlist *next;
};
+90
View File
@@ -63,6 +63,10 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
// Used by portelement structure "flags" record.
#define PORT_NOT_FOUND 0
#define PORT_FOUND 1
// Global storage for verilog parameters
struct hashdict verilogparams;
// Global storage for verilog definitions
@@ -1404,6 +1408,7 @@ skip_endmodule:
char *name; // Name of port in subcell
char *net; // Name of net connecting to port in the parent
int width; // Width of port, if port is a bus
char flags; // Used for marking if port was added into netlist
struct portelement *next;
};
@@ -1530,6 +1535,7 @@ nextinst:
new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement));
new_port->name = strsave(nexttok + 1);
new_port->width = -1;
new_port->flags = PORT_NOT_FOUND;
SkipTokComments(VLOG_DELIMITERS);
if (!match(nexttok, "(")) {
Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok);
@@ -1964,6 +1970,7 @@ nextinst:
obpinidx = -1;
while (scan != NULL) {
if (match(obpinname, scan->name)) {
scan->flags |= PORT_FOUND;
break;
}
scan = scan->next;
@@ -2056,6 +2063,89 @@ nextinst:
if (LookupObject(scan->net, CurrentCell) == NULL) Node(scan->net);
join(scan->net, obptr->name);
}
/* Before exiting the loop, check if all ports in the */
/* scan list were handled. */
if ((obptr->next == NULL) || (obptr->next->type <= FIRSTPIN)) {
for (scan = head; scan; scan = scan->next) {
if (!(scan->flags & PORT_FOUND)) {
if (tp->flags & CELL_PLACEHOLDER) {
char tempname[128];
int maxnode;
/* This pin was probably implicit in the first call */
/* and so it needs to be added to the definition. */
ReopenCellDef(modulename, filenum);
Port(scan->name);
ReopenCellDef((*CellStackPtr)->cellname, filenum);
/* obptr->next now gets the new port. Update the */
/* port number, and copy class and instance name. */
nobj = GetObject();
sprintf(tempname, "%s%s%s", obptr->instance.name,
SEPARATOR, scan->name);
nobj->name = strsave(tempname);
nobj->model.class = strsave(obptr->model.class);
nobj->instance.name = strsave(obptr->instance.name);
nobj->type = obptr->type + 1;
nobj->next = obptr->next;
nobj->node = -1;
obptr->next = nobj;
HashPtrInstall(nobj->name, nobj, &(CurrentCell->objdict));
if (LookupObject(scan->net, CurrentCell) == NULL)
Node(scan->net);
join(scan->net, nobj->name);
scan->flags |= PORT_FOUND;
/* Now any previous instance of the same cell must */
/* insert the same additional pin as a no-connect. */
/* NOTE: This should be running a callback on all */
/* cells in the file, not just CurrentCell. */
for (sobj = CurrentCell->cell; sobj && (sobj != obptr);
sobj = sobj->next) {
if (sobj->type == FIRSTPIN) {
if (match(sobj->model.class, obptr->model.class)) {
while (sobj->next->type > FIRSTPIN)
sobj = sobj->next;
/* Stop when reaching the current instance */
if (sobj->type == obptr->type + 1) break;
nobj = GetObject();
sprintf(tempname, "%s%s%s", sobj->instance.name,
SEPARATOR, scan->name);
nobj->name = strsave(tempname);
nobj->model.class = strsave(sobj->model.class);
nobj->instance.name = strsave(sobj->instance.name);
nobj->type = obptr->type + 1;
nobj->node = -1;
nobj->next = sobj->next;
sobj->next = nobj;
HashPtrInstall(nobj->name, nobj,
&(CurrentCell->objdict));
sprintf(tempname, "_noconnect_%d_", localcount++);
Node(tempname);
join(tempname, nobj->name);
Fprintf(stderr, "Note: Implicit pin %s in instance "
"%s of %s in cell %s\n",
scan->name, sobj->instance.name,
modulename, CurrentCell->name);
}
}
}
}
else {
Fprintf(stderr, "Error: Instance %s has pin %s which is "
"not in the %s cell definition.\n",
locinst, scan->name, modulename);
}
}
}
}
obptr = obptr->next;
} while (obptr != NULL && obptr->type > FIRSTPIN);
}
+2
View File
@@ -8,6 +8,8 @@ SCRIPTINSTALL = $(DESTDIR)${INSTALL_PYDIR}
include ${NETGENDIR}/defs.mak
CLEANS += lvs_manager.py
main: lvs_manager.py
tcl-main: lvs_manager.py
-819
View File
@@ -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
View File
@@ -343,7 +343,7 @@ class LVSManager(ttk.Frame):
techbase = os.path.split(techname)[1]
self.tech = os.path.splitext(techbase)[0]
self.lvs_setup = techname
self.toppane.title2_frame.layout_select.config(text = self.tech)
self.toppane.title2_frame.tech_select.config(text = self.tech)
def choose_layout(self):
try:
@@ -358,30 +358,37 @@ class LVSManager(ttk.Frame):
title = "Choose a layout cell to compare.")
if cellname != '':
print("Selected compare cell " + cellname)
self.layout = cellname
cellbase = os.path.split(cellname)[1]
self.layout = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.layout_select.config(text = self.layout)
layoutname = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.layout_select.config(text = layoutname)
fileext = os.path.splitext(cellbase)[1]
if fileext == '.mag':
self.toppane.title2_frame.layout_label.config(text = 'Layout:')
else:
self.toppane.title2_frame.layout_label.config(text = 'Layout netlist:')
def choose_netlist(self):
try:
project_path = self.rootpath
initdirname = self.rootpath + '/netlist/' + self.project + '.spi'
initdirname = self.rootpath + '/netlist/' + self.project + '.spice'
except:
print('Must choose a project first.')
return
cellname = filedialog.askopenfilename(multiple=False,
initialdir = initdirname,
filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")),
filetypes = (("Spice netlist", "*.spice"),("Verilog netlist", "*.v"),("All Files","*.*")),
title = "Choose a netlist to compare.")
if cellname != '':
print("Selected compare cell " + cellname)
self.schematic = cellname
cellbase = os.path.split(cellname)[1]
self.schematic = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.schem_select.config(text = self.schematic)
schematic_name = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.schem_select.config(text = schematic_name)
fileext = os.path.splitext(cellbase)[1]
if fileext == '.v':
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
elif fileext == '.sp' or fileext == '.spi' or fileext == '.spice' or fileext == '.spc' or fileext == '.ckt':
elif fileext == '.sp' or fileext == '.spice' or fileext == '.spi' or fileext == '.spc' or fileext == '.ckt':
self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:')
elif fileext == '.cdl':
self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:')
@@ -433,7 +440,10 @@ class LVSManager(ttk.Frame):
self.logfile = None
# Put new log file called 'lvs.log' in the mag/ subdirectory
self.logfile = open(rootpath + '/layout/lvs.log', 'w')
if os.path.exists(rootpath + '/layout'):
self.logfile = open(rootpath + '/layout/lvs.log', 'w')
else:
self.logfile = open(rootpath + '/lvs.log', 'w')
# Print some initial information to the logfile.
self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'),
doflush=True)
@@ -444,15 +454,23 @@ class LVSManager(ttk.Frame):
# Cell name is the same as project name initially
self.layout = self.project
self.schematic = self.project
self.toppane.title2_frame.layout_select.config(text = self.layout)
self.toppane.title2_frame.schem_select.config(text = self.schematic)
layname = os.path.splitext(os.path.split(self.layout)[1])[0]
self.toppane.title2_frame.layout_select.config(text = layname)
schemname = os.path.splitext(os.path.split(self.schematic)[1])[0]
self.toppane.title2_frame.schem_select.config(text = schemname)
# Update schematic button
if os.path.isfile(rootpath + '/verilog/' + self.schematic + '.v'):
if os.path.splitext(self.schematic)[1] == '.v':
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
else:
self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:')
# Update layout button
if os.path.splitext(self.layout)[1] == '.mag':
self.toppane.title2_frame.schem_label.config(text = 'Layout:')
else:
self.toppane.title2_frame.schem_label.config(text = 'Layout netlist:')
# If there is a comparison file that post-dates both netlists, load it.
self.check_lvs()
return True
@@ -567,8 +585,8 @@ class LVSManager(ttk.Frame):
# load LVS results from comp.json
project_path = self.rootpath
project_name = self.project
layout_path = project_path + '/layout/' + project_name + '.spc'
net_path = project_path + '/netlist/' + project_name + '.spi'
layout_path = project_path + '/layout/' + project_name + '.spice'
net_path = project_path + '/netlist/' + project_name + '.spice'
comp_path = project_path + '/layout/comp.json'
if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path):
@@ -599,7 +617,7 @@ class LVSManager(ttk.Frame):
mproc.stdin.write("ext2spice cthresh infinite\n")
mproc.stdin.write("ext2spice rthresh infinite\n")
mproc.stdin.write("ext2spice blackbox on\n")
mproc.stdin.write("ext2spice -o " + self.layout + ".spi\n")
mproc.stdin.write("ext2spice -o " + self.layout + ".spice\n")
mproc.stdin.write("quit -noprompt\n")
magicout = mproc.communicate()[0]
self.printwarn(magicout)
@@ -607,8 +625,8 @@ class LVSManager(ttk.Frame):
print('Failure to generate new layout netlist.')
return False
# Move .spi netlist to project_dir/netlist/lvs/
shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path)
# Move .spice netlist to project_dir/netlist/lvs/
shutil.move(project_path + '/layout/' + self.layout + '.spice', layout_path)
# Remove extraction files
for file in os.listdir(project_path + '/layout'):
if os.path.splitext(file)[1] == '.ext':
@@ -625,17 +643,37 @@ class LVSManager(ttk.Frame):
project_path = self.rootpath
project_name = self.project
comp_path = project_path + '/layout/comp.json'
# Diagnostic:
print('project_name is ' + project_name)
print('project_path is ' + project_path)
print('self.layout is ' + self.layout)
print('self.schematic is ' + self.schematic)
print('self.lvs_setup is ' + self.lvs_setup)
has_vlog = False
vlog_path = project_path + '/verilog/' + project_name + '.v'
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi'
net_path = project_path + '/netlist/schem/' + self.schematic + '.spi'
layout_src = project_path + '/layout/' + self.layout + '.mag'
if os.path.isfile(self.layout):
layout_path = self.layout
layout_src = None
else:
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spice'
layout_src = project_path + '/layout/' + self.layout + '.mag'
comp_dir = os.path.split(layout_path)[0]
comp_path = comp_dir + '/comp.json'
if os.path.isfile(self.schematic):
net_path = self.schematic
else:
net_path = project_path + '/netlist/schem/' + self.schematic + '.spice'
# Does the setup file exist (this is optional)?
if self.lvs_setup != '' and not os.path.isfile('setup.tcl'):
if self.lvs_setup == '' and not os.path.isfile('setup.tcl'):
print('No technology setup file selected.')
elif not os.path.isfile(self.lvs_setup):
print("Can't find technology setup file " + self.lvs_setup)
# Does schematic netlist exist?
if not os.path.isfile(vlog_path) and not os.path.isfile(net_path):
@@ -643,14 +681,16 @@ class LVSManager(ttk.Frame):
return
# Does LVS netlist subdirectory exist?
if not os.path.exists(project_path + '/netlist/lvs'):
os.makedirs(project_path + '/netlist/lvs')
if os.path.exists(project_path + '/netlist'):
if not os.path.exists(project_path + '/netlist/lvs'):
os.makedirs(project_path + '/netlist/lvs')
# Does layout netlist exist and is it current?
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
return False
if layout_src:
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
return False
# Final check
# Final checks
if not os.path.isfile(layout_path):
print('Error: No netlist generated from magic.')
return
@@ -662,26 +702,27 @@ class LVSManager(ttk.Frame):
with open(layout_path) as ifile:
spitext = ifile.read()
# spilines = spitext.replace(',', '|')
# with open(layout_path, 'w') as ofile:
# ofile.write(spilines)
# Check the netlist to see if the cell to match is a subcircuit. If
# not, then assume it is the top level.
layoutcell = os.path.splitext(os.path.split(self.layout)[1])[0]
is_subckt = False
subname = None
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
dutlines = spitext.replace('\n+', ' ').splitlines()
for line in dutlines:
lmatch = subrex.match(line)
if lmatch:
subname = lmatch.group(1)
if subname == self.layout:
if subname == layoutcell:
is_subckt = True
break
if is_subckt:
layout_arg = layout_path + ' ' + self.layout
layout_arg = self.layout + ' ' + layoutcell
layout_text = '"' + layout_arg + '"'
elif subname:
layout_arg = self.layout + ' ' + subname
layout_text = '"' + layout_arg + '"'
else:
layout_arg = layout_path
@@ -690,8 +731,39 @@ class LVSManager(ttk.Frame):
if has_vlog:
schem_arg = vlog_path + ' ' + self.schematic
else:
schem_arg = net_path + ' ' + self.schematic
schem_text = '"' + schem_arg + '"'
# Final checks
if not os.path.isfile(net_path):
print('Error: No netlist from schematic.')
return
with open(net_path) as ifile:
spitext = ifile.read()
# Check the netlist to see if the cell to match is a subcircuit. If
# not, then assume it is the top level.
schemcell = os.path.splitext(os.path.split(self.schematic)[1])[0]
subname = None
is_subckt = False
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
dutlines = spitext.replace('\n+', ' ').splitlines()
for line in dutlines:
lmatch = subrex.match(line)
if lmatch:
subname = lmatch.group(1)
if subname == schemcell:
is_subckt = True
break
if is_subckt:
schem_arg = self.schematic + ' ' + schemcell
schem_text = '"' + schem_arg + '"'
elif subname:
schem_arg = self.schematic + ' ' + subname
schem_text = '"' + schem_arg + '"'
else:
schem_arg = net_path
schem_text = schem_arg
# Remove any previous comparison output file
comp_out_path = os.path.splitext(comp_path)[0] + '.out'
@@ -708,7 +780,7 @@ class LVSManager(ttk.Frame):
self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs',
layout_arg, schem_arg,
self.lvs_setup, comp_out_path, '-json', '-blackbox'],
cwd=project_path + '/layout',
cwd=comp_dir,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
# This is largely unnecessary as netgen usually runs to completion very quickly.
self.watchclock(comp_path)