Merge branch 'master' into netgen-1.5

This commit is contained in:
Tim Edwards 2021-03-06 03:00:09 -05:00
commit 851a1f941e
6 changed files with 446 additions and 24 deletions

View File

@ -1 +1 @@
1.5.170
1.5.171

View File

@ -1141,6 +1141,323 @@ Tcl_Obj *ListElementClasses(int legal)
#endif
/*
*---------------------------------------------------------------------
*
* Sort the fanout lists of two formatted list entries so that they
* are as well aligned as they can be made, practically. The matching
* is ad hoc as it does not affect LVS results but only how the results
* are organized and presented in the output.
*
*---------------------------------------------------------------------
*/
void
SortFanoutLists(nlist1, nlist2)
struct FormattedList *nlist1, *nlist2;
{
struct hashdict f1hash, f2hash;
int f1, f2, total;
struct FanoutList temp;
int *matched;
char pinname[1024], pinnameA[1024], pinnameB[1024];
InitializeHashTable(&f1hash, OBJHASHSIZE);
InitializeHashTable(&f2hash, OBJHASHSIZE);
if (nlist1->fanout < nlist2->fanout) {
matched = (int *)CALLOC(nlist2->fanout, sizeof(int));
total = 0;
for (f2 = 0; f2 < nlist2->fanout; f2++) {
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
nlist2->flist[f2].name);
HashPtrInstall(pinname, (void *)((long)f2 + 1), &f2hash);
}
for (f1 = 0; f1 < nlist1->fanout; f1++) {
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
nlist1->flist[f1].name);
f2 = (int)(long)HashLookup(pinname, &f2hash);
if (f2 != 0) {
f2 -= 1;
matched[f1] = -1;
total++;
if (f2 != f1) {
temp = nlist2->flist[f2];
nlist2->flist[f2] = nlist2->flist[f1];
nlist2->flist[f1] = temp;
sprintf(pinnameA, "%s/%s", nlist2->flist[f1].model,
nlist2->flist[f1].name);
sprintf(pinnameB, "%s/%s", nlist2->flist[f2].model,
nlist2->flist[f2].name);
HashPtrInstall(pinnameA, (void *)((long)f1 + 1), &f2hash);
HashPtrInstall(pinnameB, (void *)((long)f2 + 1), &f2hash);
}
}
}
/* To do: If full pin names don't match, match by model name only */
}
else {
matched = (int *)CALLOC(nlist1->fanout, sizeof(int));
total = 0;
for (f1 = 0; f1 < nlist1->fanout; f1++) {
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
nlist1->flist[f1].name);
HashPtrInstall(pinname, (void *)((long)f1 + 1), &f1hash);
}
for (f2 = 0; f2 < nlist2->fanout; f2++) {
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
nlist2->flist[f2].name);
f1 = (int)(long)HashLookup(pinname, &f1hash);
if (f1 != 0) {
f1 -= 1;
matched[f2] = -1;
total++;
if (f1 != f2) {
temp = nlist1->flist[f1];
nlist1->flist[f1] = nlist1->flist[f2];
nlist1->flist[f2] = temp;
sprintf(pinnameA, "%s/%s", nlist1->flist[f1].model,
nlist1->flist[f1].name);
sprintf(pinnameB, "%s/%s", nlist1->flist[f2].model,
nlist1->flist[f2].name);
HashPtrInstall(pinnameA, (void *)((long)f1 + 1), &f1hash);
HashPtrInstall(pinnameB, (void *)((long)f2 + 1), &f1hash);
}
}
}
/* To do: If full pin names don't match, match by model name only */
}
FREE(matched);
HashKill(&f1hash);
HashKill(&f2hash);
}
/*
*---------------------------------------------------------------------
*
* Determine the match between two entries in a bad node fragment
* according to an ad hoc metric of how many fanout entries are
* the same between the two. This is not used to match circuits
* for LVS, but is used to sort the dump of unmatched nets generated
* for an unmatched subcell, so that the end-user is not presented
* with a list in a confusingly arbitrary order.
*
* Score is normalized to 100.
* If a model/pin has an equivalent on the other size, add 1
* If a model/pin equivalent has the same count, add 1
* Total values and normalize to a 100 score for an exact match.
*
*---------------------------------------------------------------------
*/
int
NodeMatchScore(nlist1, nlist2)
struct FormattedList *nlist1, *nlist2;
{
struct hashdict f1hash, f2hash;
char pinname[1024];
int f1, f2, maxfanout;
int score = 0;
InitializeHashTable(&f1hash, OBJHASHSIZE);
InitializeHashTable(&f2hash, OBJHASHSIZE);
if (nlist1->fanout < nlist2->fanout) {
for (f2 = 0; f2 < nlist2->fanout; f2++) {
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
nlist2->flist[f2].name);
HashPtrInstall(pinname, (void *)((long)f2 + 1), &f2hash);
}
for (f1 = 0; f1 < nlist1->fanout; f1++) {
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
nlist1->flist[f1].name);
f2 = (int)(long)HashLookup(pinname, &f2hash);
if (f2 != 0) {
f2 -= 1;
score++;
if (nlist1->flist[f1].count == nlist2->flist[f2].count)
score++;
}
}
}
else {
for (f1 = 0; f1 < nlist1->fanout; f1++) {
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
nlist1->flist[f1].name);
HashPtrInstall(pinname, (void *)((long)f1 + 1), &f1hash);
}
for (f2 = 0; f2 < nlist2->fanout; f2++) {
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
nlist2->flist[f2].name);
f1 = (int)(long)HashLookup(pinname, &f1hash);
if (f1 != 0) {
f1 -= 1;
score++;
if (nlist2->flist[f2].count == nlist1->flist[f1].count)
score++;
}
}
}
HashKill(&f1hash);
HashKill(&f2hash);
maxfanout = (nlist1->fanout < nlist2->fanout) ? nlist2->fanout : nlist1->fanout;
score = (50 * score) / maxfanout;
return score;
}
/*
*---------------------------------------------------------------------
*
* Sort node list 2 to match the entries in list 1, to the extent
* possible. Exact name matching is preferred, followed by matching
* of the largest percentage of components.
*
*---------------------------------------------------------------------
*/
void SortUnmatchedLists(nlists1, nlists2, n1max, n2max)
struct FormattedList **nlists1, **nlists2;
int n1max, n2max;
{
struct FormattedList *temp;
int n1, n2;
int *matched, total, best, ibest;
struct hashdict n1hash, n2hash;
InitializeHashTable(&n1hash, OBJHASHSIZE);
InitializeHashTable(&n2hash, OBJHASHSIZE);
if (n1max < n2max) {
matched = (int *)CALLOC(n2max, sizeof(int));
total = 0;
for (n2 = 0; n2 < n2max; n2++)
HashPtrInstall(nlists2[n2]->name, (void *)((long)n2 + 1), &n2hash);
/* Match by name */
for (n1 = 0; n1 < n1max; n1++) {
n2 = (int)(long)HashLookup(nlists1[n1]->name, &n2hash);
if (n2 != 0) {
n2 -= 1;
matched[n1] = -1;
total++;
if (n2 != n1) {
temp = nlists2[n2];
nlists2[n2] = nlists2[n1];
nlists2[n1] = temp;
HashPtrInstall(nlists2[n1]->name, (void *)((long)n1 + 1), &n2hash);
HashPtrInstall(nlists2[n2]->name, (void *)((long)n2 + 1), &n2hash);
SortFanoutLists(nlists1[n1], nlists2[n1]);
}
}
}
/* For all nets that didn't match by name, match by content */
#if 0
/* This is ifdef'd out because the improvement in the presentation
* of the output is minimal, but the amount of computation is huge.
* There are numerous ways to optimize this.
*/
if (total < n1max) {
for (n1 = 0; n1 < n1max; n1++) {
if (matched[n1] != -1) {
best = 0;
ibest = -1;
for (n2 = 0; n2 < n2max; n2++) {
if (matched[n2] != -1) {
matched[n2] = NodeMatchScore(nlists1[n1], nlists2[n2]);
if (matched[n2] > best) {
best = matched[n2];
ibest = n2;
}
}
}
if (ibest >= 0) {
matched[n1] = -1;
temp = nlists2[ibest];
nlists2[ibest] = nlists2[n1];
nlists2[n1] = temp;
SortFanoutLists(nlists1[n1], nlists2[n1]);
}
}
}
}
#endif
}
else {
matched = (int *)CALLOC(n1max, sizeof(int));
total = 0;
for (n1 = 0; n1 < n1max; n1++)
HashPtrInstall(nlists1[n1]->name, (void *)((long)n1 + 1), &n1hash);
for (n2 = 0; n2 < n2max; n2++) {
n1 = (int)(long)HashLookup(nlists2[n2]->name, &n1hash);
if (n1 != 0) {
n1 -= 1;
matched[n2] = -1;
total++;
if (n1 != n2) {
temp = nlists1[n1];
nlists1[n1] = nlists1[n2];
nlists1[n2] = temp;
HashPtrInstall(nlists1[n1]->name, (void *)((long)n1 + 1), &n1hash);
HashPtrInstall(nlists1[n2]->name, (void *)((long)n2 + 1), &n1hash);
SortFanoutLists(nlists2[n2], nlists1[n2]);
}
}
}
/* For all nets that didn't match by name, match by content */
#if 0
/* This is ifdef'd out because the improvement in the presentation
* of the output is minimal, but the amount of computation is huge.
* There are numerous ways to optimize this.
*/
if (total < n2max) {
for (n2 = 0; n2 < n2max; n2++) {
if (matched[n2] != -1) {
best = 0;
ibest = -1;
for (n1 = 0; n1 < n1max; n1++) {
if (matched[n1] != -1) {
matched[n1] = NodeMatchScore(nlists2[n2], nlists1[n1]);
if (matched[n1] > best) {
best = matched[n1];
ibest = n1;
}
}
}
if (ibest >= 0) {
matched[n2] = -1;
temp = nlists1[ibest];
nlists1[ibest] = nlists1[n2];
nlists1[n2] = temp;
SortFanoutLists(nlists2[n2], nlists1[n2]);
}
}
}
}
#endif
}
FREE(matched);
HashKill(&n1hash);
HashKill(&n2hash);
}
/*
*---------------------------------------------------------------------
*---------------------------------------------------------------------
@ -1219,8 +1536,9 @@ void FormatIllegalElementClasses()
n2++;
}
}
Fprintf(stdout, "\n");
SortUnmatchedLists(elist1, elist2, n1, n2);
Fprintf(stdout, "\n");
for (n = 0; n < ((n1 > n2) ? n1 : n2); n++) {
if (n != 0) {
for (i = 0; i < left_col_end; i++) *(ostr + i) = ' ';
@ -1522,6 +1840,12 @@ void FormatIllegalNodeClasses()
ostr = CALLOC(right_col_end + 2, sizeof(char));
found = 0;
/*
* To do: match net names across partitions, to make it much clearer how
* two nets are mismatched, when they have been dropped into different
* partitions.
*/
for (nscan = NodeClasses; nscan != NULL; nscan = nscan->next)
if (!(nscan->legalpartition)) {
struct Node *N;
@ -1574,8 +1898,9 @@ void FormatIllegalNodeClasses()
n2++;
}
}
Fprintf(stdout, "\n");
SortUnmatchedLists(nlists1, nlists2, n1, n2);
Fprintf(stdout, "\n");
for (n = 0; n < ((n1 > n2) ? n1 : n2); n++) {
if (n != 0) {
for (i = 0; i < left_col_end; i++) *(ostr + i) = ' ';
@ -5363,13 +5688,21 @@ PropertyMatch(struct objlist *ob1, int file1,
/* WIP---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)
{
struct objlist *node1, *node2;
node1 = Circuit1->nodename_cache[tp1->node];
node2 = Circuit2->nodename_cache[tp2->node];
if (file1 == Circuit1->file)
node1 = Circuit1->nodename_cache[tp1->node];
else
node1 = Circuit2->nodename_cache[tp1->node];
if (file2 == Circuit1->file)
node2 = Circuit1->nodename_cache[tp2->node];
else
node2 = Circuit2->nodename_cache[tp2->node];
if (node1->instance.flags != node2->instance.flags)
{

View File

@ -50,6 +50,7 @@ int NextNode;
int Composition = NONE;
int QuickSearch = 0;
int GlobalParallelNone = FALSE;
int GlobalParallelOpen = TRUE;
int AddToExistingDefinition = 0; /* default: overwrite cell when reopened */
@ -3164,10 +3165,13 @@ int CombineParallel(char *model, int file)
/* can be treated as equivalent for the purpose of parallelization. */
nodecount = (int *)CALLOC((tp->nodename_cache_maxnodenum + 1), sizeof(int));
for (ob = tp->cell; ob; ob = ob->next) {
if (ob->node >= 0)
if (ob->type != NODE)
nodecount[ob->node]++;
if (GlobalParallelOpen) {
for (ob = tp->cell; ob; ob = ob->next) {
if (ob->node >= 0)
if (ob->type != NODE)
nodecount[ob->node]++;
}
}
lob = NULL;

View File

@ -149,6 +149,7 @@ extern int NoOutput; /* set this to 1 to disable stdout output */
extern int Composition; /* direction of composition */
extern int UnixWildcards; /* TRUE if *,?,{},[] only; false if full REGEXP */
extern int GlobalParallelNone; /* If TRUE, don't parallel combine any cells */
extern int GlobalParallelOpen; /* If TRUE, parallel combine cells w/no-connects */
/* magic internal flag to restrict searches to recently placed cells */
extern int QuickSearch;
/* does re"CellDef"ing a cell add to it or overwrite it??? */

View File

@ -352,7 +352,7 @@ proc netgen::convert_to_json {filename lvs_final} {
close $fjson
}
#----------------------------------------------------------------
#-----------------------------------------------------------------------
# Define the "lvs" command as a way of calling the netgen options
# for standard compare, essentially the same as the old "netcomp"
# standalone program.
@ -361,14 +361,20 @@ proc netgen::convert_to_json {filename lvs_final} {
# although if the cells have not been read in yet, then the
# original syntax of filename or {filename cellname} is required.
#
# "args" is passed to verify and may therefore contain only the
# value "-list" or nothing. If "-list", then output is returned
# as a nested list.
#----------------------------------------------------------------
# "args" may be "-list", "-json", or "-blackbox".
# "-list" returns output as a nested list.
# "-json" creates a .json-format output file in addition to stdout.
# "-blackbox" treats empty cells as black-box entries.
# "-noflatten={list}" is a list of cells not to flatten if mismatched.
# i.e., the cells are expected to match and any mismatch cannot be
# expected to be resolved by flattening the contents of the mismatched
# cells.
#-----------------------------------------------------------------------
proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
set dolist 0
set dojson 0
set noflat {}
foreach arg $args {
if {$arg == "-list"} {
puts stdout "Generating list result"
@ -382,6 +388,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
} elseif {$arg == "-blackbox"} {
puts stdout "Treating empty subcircuits as black-box cells"
netgen::model blackbox on
} elseif {[string first "-noflatten=" $arg] == 0} {
set noflat [string trim [string range $arg 11 end] \"\{\}]
puts stdout "Will not flatten these subcells: $noflat"
}
}
@ -501,6 +510,7 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
return
}
set properr {}
set matcherr {}
set pinsgood 0
while {$endval != {}} {
if {$dolist == 1} {
@ -529,9 +539,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
# Flatten the non-matching subcircuit (but not the top-level cells)
if {[netgen::print queue] != {}} {
netgen::log put " Flattening non-matched subcircuits $endval"
netgen::flatten class "[lindex $endval 0] $fnum1"
netgen::flatten class "[lindex $endval 1] $fnum2"
if {([lsearch $noflat [lindex $endval 0]] == -1) &&
([lsearch $noflat [lindex $endval 1]] == -1)} {
netgen::log put " Flattening non-matched subcircuits $endval"
netgen::flatten class "[lindex $endval 0] $fnum1"
netgen::flatten class "[lindex $endval 1] $fnum2"
} else {
netgen::log put " Continuing with black-boxed subcircuits $endval"
lappend matcherr [lindex $endval 0]
# Match pins
netgen::log echo off
if {$dolist == 1} {
set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
} else {
set result [equate -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
}
if {$result != 0} {
equate classes "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"
}
set pinsgood $result
netgen::log echo on
}
}
} else {
# Match pins
@ -554,9 +585,32 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
} else {
# Flatten the non-matching subcircuit (but not the top-level cells)
if {[netgen::print queue] != {}} {
netgen::log put " Flattening non-matched subcircuits $endval"
netgen::flatten class "[lindex $endval 0] $fnum1"
netgen::flatten class "[lindex $endval 1] $fnum2"
if {([lsearch $noflat [lindex $endval 1]] == -1) &&
([lsearch $noflat [lindex $endval 1]] == -1)} {
netgen::log put " Flattening non-matched subcircuits $endval"
netgen::flatten class "[lindex $endval 0] $fnum1"
netgen::flatten class "[lindex $endval 1] $fnum2"
} else {
netgen::log put " Continuing with black-boxed subcircuits $endval"
lappend matcherr [lindex $endval 0]
netgen::log put " Continuing with black-boxed subcircuits $endval"
lappend matcherr [lindex $endval 0]
# Match pins
netgen::log echo off
if {$dolist == 1} {
set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
} else {
set result [equate -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
}
if {$result != 0} {
equate classes "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"
}
set pinsgood $result
netgen::log echo on
}
}
}
netgen::log echo off
@ -579,6 +633,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
if {$properr != {}} {
netgen::log put "The following cells had property errors: $properr\n"
}
if {$matcherr != {}} {
netgen::log put "The following subcells failed to match: $matcherr\n"
}
if {$dolog} {
netgen::log end
}

View File

@ -2827,11 +2827,13 @@ _netcmp_equate(ClientData clientData,
char *name1 = NULL, *name2 = NULL, *optstart;
struct nlist *tp1, *tp2, *SaveC1, *SaveC2;
struct objlist *ob1, *ob2;
struct ElementClass *saveEclass = NULL;
struct NodeClass *saveNclass = NULL;
int file1, file2;
int i, l1, l2, ltest, lent, dolist = 0;
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
Tcl_Obj *tobj1, *tobj2, *tobj3;
if (objc > 1) {
while (objc > 1) {
optstart = Tcl_GetString(objv[1]);
if (*optstart == '-') optstart++;
if (!strcmp(optstart, "list")) {
@ -2839,6 +2841,13 @@ _netcmp_equate(ClientData clientData,
objv++;
objc--;
}
else if (!strcmp(optstart, "force")) {
doforce = 1;
objv++;
objc--;
}
else
break;
}
if ((objc != 2) && (objc != 4) && (objc != 6)) {
@ -2996,6 +3005,12 @@ _netcmp_equate(ClientData clientData,
break;
case PINS_IDX:
if ((ElementClasses != NULL) && (doforce == TRUE)) {
saveEclass = ElementClasses;
saveNclass = NodeClasses;
ElementClasses = NULL;
NodeClasses = NULL;
}
if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) {
if (CurrentCell == NULL) {
Fprintf(stderr, "Equate elements: no current cell.\n");
@ -3058,6 +3073,12 @@ _netcmp_equate(ClientData clientData,
/* Recover temporarily set global variables (see above) */
Circuit1 = SaveC1;
Circuit2 = SaveC2;
/* Recover ElementClasses if forcing pins on mismatched circuits */
if (doforce == TRUE) {
ElementClasses = saveEclass;
NodeClasses = saveNclass;
}
}
break;
@ -3417,9 +3438,15 @@ _netcmp_property(ClientData clientData,
GlobalParallelNone = FALSE;
SetParallelCombine(TRUE);
}
else if (!strcmp(Tcl_GetString(objv[2]), "connected")) {
GlobalParallelOpen = FALSE;
}
else if (!strcmp(Tcl_GetString(objv[2]), "open")) {
GlobalParallelOpen = TRUE;
}
else {
Tcl_SetResult(interp, "Bad option, should be property parallel none|all",
NULL);
Tcl_SetResult(interp, "Bad option, should be property parallel "
"none|all|connected", NULL);
return TCL_ERROR;
}
return TCL_OK;