Compare commits

...
25 Commits
Author SHA1 Message Date
Tim Edwards cf6dd2a638 Merge branch 'master' into netgen-1.5 2021-07-16 03:00:11 -04:00
Tim Edwards a332c23524 Corrected an error in "series_sort" that will overwrite memory
randomly if combining series devices that do not have an "S"
property (which is the typical case).  This will normally result in
a crash.
2021-07-15 16:03:43 -04:00
Tim Edwards b6d896e73f Merge branch 'master' into netgen-1.5 2021-07-12 03:00:10 -04:00
Tim Edwards 064195ecc9 Reinstated some code that had been commented out that prints pin
information for a non-matching pin in circuit1 and generates a
proxy pin in circuit2---there is some case where this is redundant,
I think, but I need to find the example.
2021-07-11 17:04:50 -04:00
Tim Edwards 88d53fab15 Correction to the verilog parser to recognize modifiers such as
"~", "!", or "-" in front of variable names in a pin list that would
render the module behavioral verilog.
2021-07-11 12:06:16 -04:00
Tim Edwards c287b6cd28 A previous attempt (in revision 193) to make the "lvs" script stop
saying that pins were mismatched when pin matching was never run
accidentally resulted in pin matching not being applied to black-box
entries.  This has been corrected.
2021-07-11 10:58:30 -04:00
Tim Edwards 9251ce2a48 Merge branch 'master' into netgen-1.5 2021-07-11 03:00:33 -04:00
Tim Edwards 0a0a6bcf63 Modified the handling of missing pins (again) such that netgen
continues to allow missing pins to match unconnected pins, but
*only* on subcircuits below the top level.  This essentially forces
layouts to separate merged pins with metal resistors, although
there should be an option in magic's ext2spice routine that allows
"equiv" statements, when declaring equivalence of two ports, to be
replaced by a zero volt source or zero ohm ideal resistor.
2021-07-10 13:54:14 -04:00
Tim Edwards 72ef2f2637 Corrected the pin matching so that it runs the same loop on unmatched
pins on non-black-boxed circuits as it does not black-boxed circuits,
but specifically looking for pins that are disconnected on both sides,
since those do not appear in the node list and are not otherwise
handled.  Otherwise, disconnected pins will appear to have disappeared
from the first netlist.
2021-07-10 11:25:07 -04:00
Tim Edwards aa82164c08 Merge branch 'master' into netgen-1.5 2021-07-09 03:00:34 -04:00
Tim Edwards 287f5963d1 Corrected an error that crept into the netgen.tcl script that causes
the "failed pin matching" error message to appear for cells mismatching
topology (in which case pin matching is never done).
2021-07-08 08:56:52 -04:00
Tim Edwards fb7876c7a6 Merge branch 'master' into netgen-1.5 2021-07-03 03:00:13 -04:00
Tim Edwards a984ac1a4d Corrected an error in a recent update that handles the case where
a final parallel or series combination needs to be done but there
are still multiple property records.  The multiplier was being
incorrectly applied twice, causing an automatic mismatch in
parameter values.
2021-07-02 10:51:44 -04:00
Tim Edwards e31caa3500 Merge branch 'master' into netgen-1.5 2021-06-26 03:00:10 -04:00
Tim Edwards 738c1f7b37 Corrected an error probably introduced into the code with the handling
of multiple devices during flattening, that will skip over a node
record at the end of a subcircuit call being flattened and therefore
remove it from the netlist.
2021-06-25 13:16:42 -04:00
Tim Edwards c3cf6c3765 Made another correction that prevents netgen from truncating the pin
list that it prints in the side-by-side element mismatch comparison
for an element, when there is no node record associated with the pin
connection.  This makes the output clearer.
2021-06-25 12:35:11 -04:00
Tim Edwards 99dcc20c0a Corrected MatchPins so that it returns an error code of 0 when pins are
swapped, so that if pin names are swapped on the top level, netgen will
report this as a final error message.  Otherwise, the mismatch is only
reported back in the pin list where it is not obvious.
2021-06-25 10:27:24 -04:00
Tim Edwards c1355bee45 Merge branch 'master' into netgen-1.5 2021-06-25 03:00:10 -04:00
Tim Edwards 4bbc496749 Corrected an error in the "run converge" and "run resolve" methods.
The algorithm is to run without exhaustive subdivision until the
last step because this is much faster.  The final iteration must
be run with exhaustive subdivision on, or else it is possible to
have cells with swapped pins matching.  The routines that resolve
automorphisms were setting exhaustive subdivision for the final
iteration.  But simple "run converge" and "run resolve" were not.
2021-06-24 14:53:24 -04:00
Tim Edwards 4fb8b59a2f Merge branch 'master' into netgen-1.5 2021-06-19 03:00:10 -04:00
Tim Edwards c4f03eabaf Corrected an error not checking for running off the end of a list,
in code from a recent commit.
2021-06-18 21:23:21 -04:00
Tim Edwards 6a555ad6ed Added a missing method from parallel/series matching which is to
add properties across multiple property records in the last matching
step, if there are still multiple properties and the values can be
combined.  Previously, netgen had been assuming that there would only
be one property record left at this point, which is not true.  This
shows up particularly for BSIM fingered devices, since "nf" is
ignored.
2021-06-18 10:44:44 -04:00
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
11 changed files with 1134 additions and 110 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.187
1.5.196
+22 -8
View File
@@ -257,7 +257,7 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
struct objlist *ParentProps, *CurrentProp;
struct objlist *NextObj, *LastObj, *prepp;
struct objlist *ChildObjList, *ChildListEnd;
struct objlist *ChildStart, *ChildEnd, *ParentEnd;
struct objlist *ChildStart, *ChildEnd, *ParentEnd, *ParentNext;
struct nlist *ThisCell;
struct nlist *ChildCell;
struct objlist *tmp, *ob2, *ob3;
@@ -344,7 +344,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
/* 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 &&
/* Stop on a node or the next instance */
((ParentEnd->next->type > FIRSTPIN) ||
(ParentEnd->next->type == PROPERTY));
ParentEnd = ParentEnd->next);
/* Not primitive, so need to flatten this instance */
@@ -445,6 +448,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
}
else tmp = tmp->next;
}
if (ChildStart == NULL) {
if (ChildListEnd == ChildEnd) ChildListEnd = NULL;
ChildEnd = NULL;
}
}
/* for each element in child, prepend 'prefix' */
@@ -532,7 +539,7 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
*/
CurrentProp = CurrentProp->next;
if (CurrentProp->type != PROPERTY) break;
if ((CurrentProp == NULL) || (CurrentProp->type != PROPERTY)) break;
}
else break;
@@ -542,8 +549,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
}
/* Put the child cell at the start of ChildObjList */
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
if (ChildEnd) {
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
}
/* Pull the instance out of the parent */
@@ -552,7 +561,7 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
/* ParentParams are the very first thing in the list */
ThisCell->cell = ChildObjList;
}
else {
else if (ChildObjList) {
/* find ParentParams in ThisCell list. In most cases, LastObj */
/* should be pointing to it. */
if (LastObj && (LastObj->next == ParentParams)) {
@@ -570,12 +579,17 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
ob2->next = ChildObjList;
}
}
else {
/* The child was completely optimized out, so close list around it */
LastObj->next = ParentEnd->next;
}
/* Link end of child list into the parent */
if (ChildListEnd)
if (ChildListEnd && ParentEnd)
ChildListEnd->next = ParentEnd->next;
}
while (ParentParams != ChildListEnd->next) {
ParentNext = (ParentEnd) ? ParentEnd->next : NULL;
while (ParentParams != ParentNext) {
ob2 = ParentParams->next;
FreeObjectAndHash(ParentParams, ThisCell);
ParentParams = ob2;
+189 -85
View File
@@ -620,15 +620,15 @@ struct FormattedList *FormatBadElementFragment(struct Element *E)
for (elems = nodes[i]->node->elementlist; elems != NULL;
elems = elems->next)
count++;
elemlist->flist[k].count = count;
if (*ob->name != *ob->instance.name) // e.g., "port_match_error"
elemlist->flist[k].name = ob->name;
else
elemlist->flist[k].name = ob->name + strlen(ob->instance.name) + 1;
elemlist->flist[k].permute = (char)1;
k++;
}
elemlist->flist[k].count = count;
if (*ob->name != *ob->instance.name) // e.g., "port_match_error"
elemlist->flist[k].name = ob->name;
else
elemlist->flist[k].name = ob->name + strlen(ob->instance.name) + 1;
elemlist->flist[k].permute = (char)1;
k++;
}
else { /* handle multiple permutable pins */
struct objlist *ob2;
@@ -665,13 +665,16 @@ struct FormattedList *FormatBadElementFragment(struct Element *E)
struct ElementList *elems;
count = 0;
if (nodes[j]->node == NULL) continue; // ?
for (elems = nodes[j]->node->elementlist; elems != NULL;
elems = elems->next)
count++;
if (nodes[j]->node == NULL) /* Under what condition is the node NULL? */
count++;
else {
for (elems = nodes[j]->node->elementlist; elems != NULL;
elems = elems->next)
count++;
}
if (count >= maxcount) {
maxcount = count;
maxindex = j;
maxcount = count;
maxindex = j;
}
}
}
@@ -4118,6 +4121,7 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
sval = 1;
cval = slop = 0.0;
for (i = 0; i < run; i++) {
sl = NULL;
merge_type = MERGE_NONE;
for (p = 0;; p++) {
vl = &(obp->instance.props[p]);
@@ -4146,13 +4150,13 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
proplist[i].value = cval * (double)sval;
proplist[i].slop = slop;
proplist[i].avalue = 0;
sl->value.ival = 1;
if (sl) sl->value.ival = 1;
}
else if (merge_type == MERGE_S_PAR) {
proplist[i].value = cval / (double)sval;
proplist[i].slop = slop;
proplist[i].avalue = 0;
sl->value.ival = 1;
if (sl) sl->value.ival = 1;
}
else {
/* Components which declare no series addition method stay unsorted */
@@ -4697,7 +4701,7 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
struct objlist *ob2, *obt;
struct property *kl, *m_rec, **plist;
unsigned char **clist;
struct valuelist ***vlist, *vl, *vl2, *newvlist;
struct valuelist ***vlist, *vl, *vl2, *newvlist, critval;
proplinkptr plink, ptop;
int pcount, p, i, j, k, pmatch, ival, ctype;
double dval;
@@ -4785,12 +4789,12 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
else if (kl != NULL) {
vlist[kl->idx][i] = vl;
if (series == FALSE) {
if (kl->merge & (MERGE_P_ADD | MERGE_P_PAR))
if (kl->merge & (MERGE_P_ADD | MERGE_P_PAR | MERGE_P_CRIT))
clist[kl->idx][i] = kl->merge &
(MERGE_P_ADD | MERGE_P_PAR | MERGE_P_CRIT);
}
else if (series == TRUE) {
if (kl->merge & (MERGE_S_ADD | MERGE_S_PAR))
if (kl->merge & (MERGE_S_ADD | MERGE_S_PAR | MERGE_S_CRIT))
clist[kl->idx][i] = kl->merge &
(MERGE_S_ADD | MERGE_S_PAR | MERGE_S_CRIT);
}
@@ -4945,24 +4949,44 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
// (if any)
if (comb == TRUE) {
int mult;
int mult, cidx = -1;
struct valuelist *avl, *cvl = NULL;
critval.type = PROP_ENDLIST;
critval.value.dval = 0.0;
for (i = 0; i < run; i++) {
avl = NULL;
if (vlist[0][i] == NULL) continue;
mult = vlist[0][i]->value.ival;
if (mult > 1) {
changed = 0;
changed = 0;
/* For all properties that are not M, S, or crit, */
/* combine as specified by the merge type of the property. */
/* For all properties that are not M, S, or crit, */
/* combine as specified by the merge type of the property. */
for (p = 1; p < pcount; p++) {
vl = vlist[p][i];
ctype = clist[p][i];
for (p = 1; p < pcount; p++) {
vl = vlist[p][i];
ctype = clist[p][i];
/* critical properties never combine */
if ((series == TRUE) && (ctype & MERGE_S_CRIT)) continue;
if ((series == FALSE) && (ctype & MERGE_P_CRIT)) continue;
/* critical properties never combine, but track them */
if ((series == TRUE) && (ctype & MERGE_S_CRIT)) {
if ((vl->type != critval.type) || (vl->value.dval != critval.value.dval))
{
critval.type = vl->type;
critval.value = vl->value;
cidx = i;
}
continue;
}
if ((series == FALSE) && (ctype & MERGE_P_CRIT)) {
if ((vl->type != critval.type) || (vl->value.dval != critval.value.dval))
{
critval.type = vl->type;
critval.value = vl->value;
cidx = i;
}
continue;
}
if (mult > 1) {
if (ctype & (MERGE_S_ADD | MERGE_P_ADD)) {
if (vl->type == PROP_INTEGER)
vl->value.ival *= mult;
@@ -4982,14 +5006,68 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
vlist[0][i]->value.ival = 1;
changed += mult;
}
if (changed > 0) {
if (series)
Printf("Combined %d series devices.\n", changed);
else
Printf("Combined %d parallel devices.\n", changed);
}
if (ctype & (MERGE_S_ADD | MERGE_P_ADD | MERGE_S_PAR | MERGE_P_PAR))
avl = vl;
}
if (cidx == i) cvl = avl;
/* Sorting should have put all records with the same critical */
/* value together sequentially. So if there are still */
/* multiple property records, then merge them into the first */
/* record with the same critical property value. */
if ((i > 0) && (cidx >= 0) && (cidx < i)) {
for (p = 1; p < pcount; p++) {
vl = vlist[p][i];
ctype = clist[p][i];
if (ctype & (MERGE_S_ADD | MERGE_P_ADD)) {
vlist[0][i]->value.ival = 0; /* set M to 0 */
if (cvl && (cvl->type == PROP_INTEGER))
{
if (vl->type == PROP_INTEGER)
cvl->value.ival += vl->value.ival;
else {
cvl->type = PROP_DOUBLE;
cvl->value.dval = (double)cvl->value.ival + vl->value.dval;
}
}
else if ((cvl && vl->type == PROP_DOUBLE))
{
if (vl->type == PROP_INTEGER)
cvl->value.dval += (double)vl->value.ival;
else
cvl->value.dval += vl->value.dval;
}
}
else if (ctype & (MERGE_S_PAR | MERGE_P_PAR)) {
vlist[0][i]->value.ival = 0; /* set M to 0 */
/* To do parallel combination, both types need to
* be double, so recast them if they are integer.
*/
if (vl->type == PROP_INTEGER) {
vl->type = PROP_DOUBLE;
vl->value.dval = (double)(vl->value.ival);
}
if (cvl && (cvl->type == PROP_INTEGER)) {
cvl->type = PROP_DOUBLE;
cvl->value.dval = (double)cvl->value.ival;
}
if ((cvl && (vl->type == PROP_DOUBLE))) {
cvl->value.dval =
sqrt(cvl->value.dval * cvl->value.dval
+ vl->value.dval * vl->value.dval);
}
}
}
}
}
if (changed > 0) {
if (series)
Printf("Combined %d series devices.\n", changed);
else
Printf("Combined %d parallel devices.\n", changed);
}
}
}
@@ -5595,13 +5673,12 @@ PropertyMatch(struct objlist *ob1, int file1,
int do_print, int do_list, int *retval)
{
struct nlist *tc1, *tc2;
struct objlist *tp1, *tp2, *obn1, *obn2;
struct objlist *tp1, *tp2, *obn1, *obn2, *tpc;
struct property *kl1, *kl2;
struct valuelist *vl1, *vl2;
int t1type, t2type;
int t1type, t2type, run1, run2;
int i, mismatches = 0, checked_one;
int rval = 1;
int flags1, flags2;
char *inst1, *inst2;
#ifdef TCL_NETGEN
Tcl_Obj *proplist = NULL, *mpair, *mlist;
@@ -5714,22 +5791,16 @@ PropertyMatch(struct objlist *ob1, int file1,
* single node) has non-zero flags. A non-node entry in the cache
* implies a node with zero flags.
*/
flags1 = flags2 = 0;
if ((node1->type <= NODE) && (node1->type >= UNIQUEGLOBAL))
flags1 = node1->instance.flags;
if ((node2->type <= NODE) && (node2->type >= UNIQUEGLOBAL))
flags2 = node2->instance.flags;
if (flags1 != flags2) {
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++;
}
}
@@ -5862,13 +5933,19 @@ PropertyMatch(struct objlist *ob1, int file1,
inst2, FALSE, FALSE, &multmatch, NULL);
if (multmatch == 1) {
/* Final attempt: Reduce M to 1 on both devices */
PropertyOptimize(tp1, tc1, 1, FALSE, TRUE);
PropertyOptimize(tp2, tc2, 1, FALSE, TRUE);
run1 = run2 = 0;
for (tpc = tp1; tpc && (tpc->type == PROPERTY); tpc = tpc->next) run1++;
for (tpc = tp2; tpc && (tpc->type == PROPERTY); tpc = tpc->next) run2++;
PropertyOptimize(tp1, tc1, run1, FALSE, TRUE);
PropertyOptimize(tp2, tc2, run2, FALSE, TRUE);
}
else if (multmatch == 2) {
/* Final attempt: Reduce S to 1 on both devices */
PropertyOptimize(tp1, tc1, 1, TRUE, TRUE);
PropertyOptimize(tp2, tc2, 1, TRUE, TRUE);
run1 = run2 = 0;
for (tpc = tp1; tpc && (tpc->type == PROPERTY); tpc = tpc->next) run1++;
for (tpc = tp2; tpc && (tpc->type == PROPERTY); tpc = tpc->next) run2++;
PropertyOptimize(tp1, tc1, run1, TRUE, TRUE);
PropertyOptimize(tp2, tc2, run2, TRUE, TRUE);
}
#ifdef TCL_NETGEN
mlist =
@@ -6815,6 +6892,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;
@@ -6833,6 +6915,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) {
@@ -7142,8 +7225,13 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
snprintf(ostr, left_col_end, "%s", obn->name);
if ((*matchfunc)(obn->name, obp->name))
snprintf(ostr + left_col_end + 1, left_col_end, "%s", obp->name);
else
else {
snprintf(ostr + left_col_end + 1, left_col_end, "%s **Mismatch**", obp->name);
/* Pins with different names are on different nets,
* so this should trigger an error return code.
*/
result = 0;
}
for (m = 0; m < right_col_end + 1; m++)
if (*(ostr + m) == '\0') *(ostr + m) = ' ';
Fprintf(stdout, ostr);
@@ -7256,35 +7344,44 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
/* so apply only to black-box (CELL_PLACEHOLDER) entries. */
/* (Semi-hack: Allow "!" global flag) */
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;
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 unconnected pins */
if (!strncmp(name1, "proxy", 5) && (ob2->node == -1)) name1 +=5;
if (!strncmp(name2, "proxy", 5) && (ob1->node == -1)) name2 +=5;
if ((*matchfunc)(name1, name2)) {
/* If both sides have unconnected nodes, then pins with */
/* matching names are an automatic match. Otherwise, if */
/* matching black-box entries, then pins are always */
/* matched by name. */
if (((ob1->node == -1) && (ob2->node == -1)) ||
(((tc1->flags & CELL_PLACEHOLDER) &&
(tc2->flags & CELL_PLACEHOLDER)) ||
(NodeClasses == NULL))) {
if ((*matchfunc)(name1, name2)) {
ob2->model.port = i; /* save order */
*(cover + i) = (char)1;
@@ -7311,13 +7408,15 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
Tcl_NewStringObj(ob2->name, -1));
}
#endif
if (bangptr2) *bangptr2 = '!';
break;
}
if (bangptr2) *bangptr2 = '!';
j++;
}
if (bangptr2) *bangptr2 = '!';
j++;
}
ob1 = ob1->next;
}
ob1 = ob1->next;
if (bangptr1) *bangptr1 = '!';
}
@@ -7369,6 +7468,12 @@ 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;
/* On the top level, missing pins are an error, even if */
/* they appear to match unconnected pins on the other side. */
if (CompareQueue == NULL)
result = 0;
#ifdef TCL_NETGEN
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
@@ -7456,7 +7561,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
}
if (ob1 == NULL || ob1->type != PORT || ob1->node >= 0
|| (ob1->node < 0 && tc1->class == CLASS_MODULE)) {
|| (ob1->node < 0 && tc1->class == CLASS_MODULE)
|| (ob1->node < 0 && ob1->model.port == -1)) {
/* Add a proxy pin to tc2 */
obn = (struct objlist *)CALLOC(1, sizeof(struct objlist));
@@ -7473,8 +7579,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
obn->instance.name = NULL;
obn->node = -1;
#if 0
/* Note: This pin has already been accounted for */
/* Note: Has this pin already been accounted for? */
if (Debug == 0) {
if (strcmp(ob1->name, "(no pins)")) {
for (m = 0; m < left_col_end; m++) *(ostr + m) = ' ';
@@ -7490,7 +7595,6 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
Fprintf(stderr, "No netlist match for cell %s pin %s\n",
tc1->name, ob1->name);
}
#endif
if (ob2 == tc2->cell) {
obn->next = ob2;
+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 -4
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,9 +3260,7 @@ int CombineParallel(char *model, int file)
if ((ob2->node >= 0) && (nodecount[ob2->node] == 1))
{
nob = (tp->nodename_cache)[ob2->node];
/* NOTE: nob must be a pin type or NODE, so it's */
/* okay to set the instance.flags record for it. */
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;
};
+8
View File
@@ -1577,6 +1577,14 @@ nextinst:
}
new_port->net = wire_bundle;
}
else if (nexttok[0] == '~' || nexttok[0] == '!' || nexttok[0] == '-') {
/* All of these imply that the signal is logically manipulated */
/* in turn implying behavioral code. */
Printf("Module '%s' is not structural verilog, "
"making black-box.\n", model);
SetClass(CLASS_MODULE);
goto skip_endmodule;
}
else
new_port->net = strsave(nexttok);
+891
View File
@@ -0,0 +1,891 @@
#!/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.tech_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)
self.layout = cellname
cellbase = os.path.split(cellname)[1]
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 + '.spice'
except:
print('Must choose a project first.')
return
cellname = filedialog.askopenfilename(multiple=False,
initialdir = initdirname,
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]
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 == '.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:')
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
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)
# 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
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.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
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 + '.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):
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 + ".spice\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 .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':
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
# 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'
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'):
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):
print('Error: No schematic netlist or verilog netlist.')
return
# Does LVS netlist subdirectory exist?
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 layout_src:
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
return False
# Final checks
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()
# 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 == layoutcell:
is_subckt = True
break
if is_subckt:
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
layout_text = layout_arg
if has_vlog:
schem_arg = vlog_path + ' ' + self.schematic
else:
# 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'
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=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)
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()
+8 -4
View File
@@ -532,7 +532,7 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
}
set properr {}
set matcherr {}
set pinsgood 0
set pinsgood -1
while {$endval != {}} {
if {$dolist == 1} {
netgen::run -list converge
@@ -586,7 +586,6 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
}
}
} else {
# Match pins
netgen::log echo off
if {$dolist == 1} {
set result [equate -list pins "$fnum1 [lindex $endval 0]" \
@@ -599,7 +598,11 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
equate classes "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"
}
set pinsgood $result
# If $uresult == -1 then these are black-box entries and
# $pinsgood should not be set to the resulting value.
if {$uresult > 0} {
set pinsgood $result
}
netgen::log echo on
}
if {$uresult == 2} {lappend properr [lindex $endval 0]}
@@ -647,7 +650,8 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
puts stdout "Result: " nonewline
netgen::log echo on
if {$pinsgood == 0} {
netgen::log put "The top level cell failed pin matching.\n"
# NOTE: Need to disambiguate these two cases. . .
netgen::log put "Cells failed matching, or top level cell failed pin matching.\n"
} else {
verify only
}
+4
View File
@@ -2428,6 +2428,8 @@ _netcmp_run(ClientData clientData,
else {
enable_interrupt();
while (!Iterate() && !InterruptPending);
ExhaustiveSubdivision = 1;
while (!Iterate() && !InterruptPending);
if (dolist) {
result = _netcmp_verify(clientData, interp, 2, objv - 1);
}
@@ -2446,6 +2448,8 @@ _netcmp_run(ClientData clientData,
else {
enable_interrupt();
while (!Iterate() && !InterruptPending);
ExhaustiveSubdivision = 1;
while (!Iterate() && !InterruptPending);
automorphisms = VerifyMatching();
if (automorphisms == -1)
Fprintf(stdout, "Netlists do not match.\n");