Compare commits

...
15 Commits
Author SHA1 Message Date
Tim Edwards 45164bbf98 Merge branch 'master' into netgen-1.5 2025-11-08 02:00:04 -05:00
R. Timothy Edwards 2ee286efb4 Corrected the pin permutation check for pin matching; previously,
this was not doing the correct cross-check, instead looking in the
same netlist for the permutable pin and checking its node number,
which is useless since the node number is the same by definition
for permutable pins.  This error would result in occasional false
negative results during pin matching, showing matching where pins
are actually not matched.
2025-11-07 14:06:22 -05:00
Tim Edwards 46cb8d6637 Merge branch 'master' into netgen-1.5 2025-10-24 02:00:04 -04:00
R. Timothy Edwards dae6919d4f Updating the version to go along with the merge of pull request 2025-10-23 09:54:36 -04:00
D. Mitch Bailey 017bdc6e48 Changed nested to static variable. Otherwise gets reset with each line.
Signed-off-by: D. Mitch Bailey <[email protected]>
2025-10-23 08:30:08 +00:00
Tim Edwards 2825b786b7 Merge branch 'master' into netgen-1.5 2025-10-23 02:00:04 -04:00
R. Timothy Edwards b371af9235 Corrected an error that was assumed to have been fixed three years
ago (and may have been, but only under limited circumstances).  Do
to several errors, using "-noflatten" on the command line and using
"flatten prohibit" in a script would not prevent cells from being
flattened;  the "-noflatten" list needed to be used to call "flatten
prohibit", and "flatten prohibit" needed to be fixed to flag the
specified cell instead of the top level cell where it exists.
2025-10-22 10:43:00 -04:00
Tim Edwards 0af8c7ad49 Merge branch 'master' into netgen-1.5 2025-10-10 02:00:03 -04:00
R. Timothy Edwards b5432d139b Corrected a corner-case where a module with no ports in verilog
was creating an implicit net for the stand-in "(no pins)" port.
2025-10-09 10:36:41 -04:00
Tim Edwards 0aa20dcf62 Merge branch 'master' into netgen-1.5 2025-10-09 02:00:04 -04:00
R. Timothy Edwards 0e958bd45c Corrected an issue in which black-box entries (such as low-level
subcircuit devices) do not output information about mismatched
pins.  This can end up being treated as a non-error but the
mismatch should be noted in the output regardless.
2025-10-08 10:03:35 -04:00
Tim Edwards 7cbf15aab1 Merge branch 'master' into netgen-1.5 2025-10-03 02:00:02 -04:00
R. Timothy Edwards b59196fa81 Modified the SPICE file read routine to accept the CDL syntax
"*.GLOBAL" as equivalent to ".GLOBAL".  Corrected the property
matching to handle property combination when no "critical"
property is given.  Critical properties exist when one property
must remain constant and equal for other properties to combine,
such as transistor length.  But, for example, capacitors can
combine area without any restriction based on another property.
Also, corrected the property matching code to allow more than
one property to be additive (example:  capacitor area and
perimeter).  Corrected the equation for adding properties in
parallel combination.
2025-10-02 12:33:28 -04:00
Tim Edwards 1de6f88f1e Merge branch 'master' into netgen-1.5 2025-09-10 02:00:02 -04:00
R. Timothy Edwards 6e6e9fb73f Added code to catch and print an error in connectivity between a
port and an internal node which can be missed when pin permutations
are present.  Previously, that could produce a situation where
netgen would report a "port error" but otherwise list all ports
as matching.  Because the permutation handling makes this hard to
detect while generating pin correspondence output, the non-matching
pins are listed separately at the end, and only if no mismatch was
detected during output.
2025-09-09 13:45:29 -04:00
8 changed files with 201 additions and 64 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.299
1.5.306
+53 -14
View File
@@ -1756,6 +1756,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
flattenInstancesOf(name1, file1, ecomp->cell1->name);
modified1++;
}
else if (ecomp->cell1 && (ecomp->num1 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell1->name, name1, file1);
}
if (ecomp->cell2 && (ecomp->num2 > 0) &&
(!(ecomp->cell2->flags & CELL_PLACEHOLDER))) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
@@ -1764,6 +1769,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
flattenInstancesOf(name2, file2, ecomp->cell2->name);
modified2++;
}
else if (ecomp->cell2 && (ecomp->num2 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell2->name, name2, file2);
}
}
/* Reset or apply the count adjustments */
@@ -1854,6 +1864,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
flattenInstancesOf(name2, file2, ecomp->cell2->name);
modified2++;
}
else if (ecomp->cell2) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell2->name, name2, file2);
}
}
}
@@ -1920,6 +1935,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
flattenInstancesOf(name1, file1, ecomp->cell1->name);
modified1++;
}
else if (ecomp->cell1) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell1->name, name1, file1);
}
}
}
@@ -2271,13 +2291,23 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
ecompX0->cell1->file, &compdict);
if (dstr) *dstr = '[';
if ((ncomp == ecomp0X) && (ecomp0X->num2 <= ecompX0->num1)) {
Fprintf(stdout, "Flattening instances of %s in cell %s"
"(%d) makes a better match\n",
ecompX0->cell1->name, name1, file1);
flattenInstancesOf(name1, file1, ecompX0->cell1->name);
ecompX0->num1 = 0;
ecomp0X->num1 += ecompX0->num1;
modified1++;
if (!(ecompX0->cell1->flags & CELL_PLACEHOLDER)) {
Fprintf(stdout, "Flattening instances of %s in cell"
" %s (%d) makes a better match\n",
ecompX0->cell1->name, name1, file1);
flattenInstancesOf(name1, file1,
ecompX0->cell1->name);
ecompX0->num1 = 0;
ecomp0X->num1 += ecompX0->num1;
modified1++;
}
else
{
Fprintf(stdout, "Flattening instances of %s in "
"cell %s (%d) would make a better "
"match but is prohibited.\n",
ecompX0->cell1->name, name1, file1);
}
break;
}
}
@@ -2297,13 +2327,22 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
ecomp0X->cell2->file, &compdict);
if (dstr) *dstr = '[';
if ((ncomp == ecompX0) && (ecompX0->num1 <= ecomp0X->num2)) {
Fprintf(stdout, "Flattening instances of %s in cell %s"
" (%d) makes a better match\n",
ecomp0X->cell2->name, name2, file2);
flattenInstancesOf(name2, file2, ecomp0X->cell2->name);
ecomp0X->num2 = 0;
ecompX0->num2 += ecomp0X->num2;
modified2++;
if (!(ecomp0X->cell2->flags & CELL_PLACEHOLDER)) {
Fprintf(stdout, "Flattening instances of %s in cell"
" %s (%d) makes a better match\n",
ecomp0X->cell2->name, name2, file2);
flattenInstancesOf(name2, file2,
ecomp0X->cell2->name);
ecomp0X->num2 = 0;
ecompX0->num2 += ecomp0X->num2;
modified2++;
}
else {
Fprintf(stdout, "Flattening instances of %s in "
"cell %s (%d) would make a better "
"match but is prohibited.\n",
ecompX0->cell2->name, name2, file2);
}
break;
}
}
+105 -33
View File
@@ -5218,14 +5218,23 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
if (comb == TRUE) {
double pd;
int hascrit = FALSE;
int mult, cidx = -1;
struct valuelist *avl, *cvl = NULL;
struct valuelist **avl, **cvl;
struct valuelist *cvlp;
critval.type = PROP_ENDLIST;
critval.value.dval = 0.0;
/* Track properties separately so that multiple properties can be
* added together (e.g., area and perimeter)
*/
avl = (struct valuelist **)CALLOC(pcount, sizeof(struct valuelist *));
cvl = (struct valuelist **)CALLOC(pcount, sizeof(struct valuelist *));
for (i = 0; i < run; i++) {
avl = NULL;
// if (vlist[0][i] == NULL) continue;
// mult = vlist[0][i]->value.ival;
for (p = 0; p < pcount; p++) avl[p] = NULL;
if (vlist[0][i] == NULL)
mult = 1;
else
@@ -5242,6 +5251,7 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
/* critical properties never combine, but track them */
if ((series == TRUE) && (ctype & MERGE_S_CRIT)) {
hascrit = TRUE;
pd = 2 * fabs(vl->value.dval - critval.value.dval) /
(vl->value.dval + critval.value.dval);
if ((vl->type != critval.type) || (pd > kl->slop.dval))
@@ -5253,6 +5263,7 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
continue;
}
if ((series == FALSE) && (ctype & MERGE_P_CRIT)) {
hascrit = TRUE;
pd = 2 * fabs(vl->value.dval - critval.value.dval) /
(vl->value.dval + critval.value.dval);
if ((vl->type != critval.type) || (pd > kl->slop.dval))
@@ -5286,19 +5297,30 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
}
}
if (ctype & (MERGE_S_ADD | MERGE_P_ADD | MERGE_S_PAR | MERGE_P_PAR))
avl = vl;
avl[p] = vl;
}
if (cidx == i) cvl = avl;
if (hascrit == FALSE) cidx = 0; /* No critical property */
/* Each time a new critical value is found, set the location */
/* of the property record that will collect the summation or */
/* parallel combination of values. */
if (cidx == i) {
for (p = 1; p < pcount; p++) {
cvl[p] = avl[p];
}
}
/* 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. */
/* record with the same critical property value. If no */
/* critical value exists, then all records can be merged. */
if ((i > 0) && (cidx >= 0) && (cidx < i)) {
for (p = 1; p < pcount; p++) {
vl = vlist[p][i];
ctype = clist[p][i];
cvlp = cvl[p];
if (ctype & (MERGE_S_ADD | MERGE_P_ADD)) {
if (!vlist[0][i]) {
@@ -5307,21 +5329,21 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
sizeof(struct valuelist));
}
vlist[0][i]->value.ival = 0; /* set M to 0 */
if (cvl && (cvl->type == PROP_INTEGER))
if (cvlp && (cvlp->type == PROP_INTEGER))
{
if (vl->type == PROP_INTEGER)
cvl->value.ival += vl->value.ival;
cvlp->value.ival += vl->value.ival;
else {
cvl->type = PROP_DOUBLE;
cvl->value.dval = (double)cvl->value.ival + vl->value.dval;
cvlp->type = PROP_DOUBLE;
cvlp->value.dval = (double)cvlp->value.ival + vl->value.dval;
}
}
else if ((cvl && vl->type == PROP_DOUBLE))
else if ((cvlp && vl->type == PROP_DOUBLE))
{
if (vl->type == PROP_INTEGER)
cvl->value.dval += (double)vl->value.ival;
cvlp->value.dval += (double)vl->value.ival;
else
cvl->value.dval += vl->value.dval;
cvlp->value.dval += vl->value.dval;
}
}
else if (ctype & (MERGE_S_PAR | MERGE_P_PAR)) {
@@ -5331,21 +5353,22 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
sizeof(struct valuelist));
}
vlist[0][i]->value.ival = 0; /* set M to 0 */
/* To do parallel combination, both types need to
/* Parallel value combination: (X * Y) / (X + Y)
* 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 (cvlp && (cvlp->type == PROP_INTEGER)) {
cvlp->type = PROP_DOUBLE;
cvlp->value.dval = (double)cvlp->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 ((cvlp && (vl->type == PROP_DOUBLE))) {
cvlp->value.dval =
(vl->value.dval * cvlp->value.dval) /
(vl->value.dval + cvlp->value.dval);
}
}
}
@@ -5357,6 +5380,9 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
Printf("Combined %d parallel devices.\n", changed);
}
}
FREE(avl);
FREE(cvl);
}
// Remove entries with M (S) = 0
@@ -7601,6 +7627,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
int hasproxy1 = 0, hasproxy2 = 0;
int needclean1 = 0, needclean2 = 0;
int nomatch = 0;
int P1, P2;
int filenum = -1;
int *correspond;
char *ostr;
@@ -7633,6 +7660,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
return 2;
}
correspond = (int *)CALLOC((tc1->nodename_cache_maxnodenum + 1), sizeof(int));
cover = (char *)CALLOC(numnodes, sizeof(char));
numorig = numnodes;
@@ -7851,8 +7879,6 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
ob1 = tc1->cell;
correspond = (int *)CALLOC((tc1->nodename_cache_maxnodenum + 1), sizeof(int));
for (i = 0; i < numorig; i++) {
bangptr1 = strrchr(ob1->name, '!');
if (bangptr1 && (*(bangptr1 + 1) == '\0'))
@@ -7973,7 +7999,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
if (bangptr2) *bangptr2 = '!';
break;
}
else {
else if (IsPort(ob1) && IsPort(ob2)) {
struct Permutation *permute1, *permute2;
struct objlist *ob1a = NULL, *ob2a = NULL;
@@ -7983,30 +8009,28 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
*/
for (permute1 = tc1->permutes; permute1; permute1 = permute1->next) {
if ((*matchfunc)(ob1->name, permute1->pin1)) {
ob1a = LookupObject(permute1->pin1, tc1);
ob2a = LookupObject(permute1->pin2, tc2);
break;
}
else if ((*matchfunc)(ob1->name, permute1->pin2)) {
ob1a = LookupObject(permute1->pin2, tc1);
ob2a = LookupObject(permute1->pin1, tc2);
break;
}
}
for (permute2 = tc2->permutes; permute2; permute2 = permute2->next) {
if ((*matchfunc)(ob2->name, permute2->pin1)) {
ob2a = LookupObject(permute2->pin1, tc2);
ob1a = LookupObject(permute2->pin2, tc1);
break;
}
else if ((*matchfunc)(ob2->name, permute2->pin2)) {
ob2a = LookupObject(permute2->pin2, tc2);
ob1a = LookupObject(permute2->pin1, tc1);
break;
}
}
if (ob1a && ob2a) {
if ((ob1->node == ob1a->node) && (ob2->node == ob2a->node)) {
/* This should be enough to prove equivalency */
if ((correspond[ob1->node] == 0) || (correspond[ob1->node] == ob2->node)) {
correspond[ob1->node] = ob2->node; /* remember corresponding node */
if ((correspond[ob1->node] == 0) ||
(correspond[ob1->node] == ob2->node)) {
ob2->model.port = i; /* save order */
*(cover + i) = (char)1;
@@ -8172,6 +8196,14 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
hasproxy1 = 1;
HashPtrInstall(obn->name, obn, &(tc1->objdict));
/* If this is a black-box circuit, then the pin has not been output */
if (NodeClasses == NULL) {
output_string_fill(ostr);
output_string_left(ostr, "%s", "(no matching pin)");
output_string_right(ostr, "%s", ob2->name);
output_string_print(ostr);
}
}
}
@@ -8242,6 +8274,14 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
hasproxy2 = 1;
HashPtrInstall(obn->name, obn, &(tc2->objdict));
/* If this is a black-box circuit, then the pin has not been output */
if (NodeClasses == NULL) {
output_string_fill(ostr);
output_string_left(ostr, "%s", ob1->name);
output_string_right(ostr, "%s", "(no matching pin)");
output_string_print(ostr);
}
}
else if (ob1 != NULL && ob1->type == PORT && ob1->node < 0) {
@@ -8269,6 +8309,38 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
if (Debug == 0)
output_string_print_divider(ostr, FALSE);
/* Catch errors where disconnected ports get effectively hidden by */
/* pin permutations. */
if (result == 1) {
int found = 0;
for (NC = NodeClasses; NC != NULL; NC = NC->next) {
P1 = P2 = 0;
for (N1 = NC->nodes; N1 != NULL; N1 = N1->next) {
if (N1->graph == Circuit2->file)
if (IsPort(N1->object)) P2++;
if (N1->graph == Circuit1->file)
if (IsPort(N1->object)) P1++;
}
if (P1 != P2) {
if (found == 0)
Fprintf(stdout, "\nPort connection errors found:\n");
found = 1;
for (N1 = NC->nodes; N1 != NULL; N1 = N1->next) {
obn = N1->object;
if (IsPort(obn))
Fprintf(stdout, " %s (%d)\n", obn->name, N1->graph);
else
Fprintf(stdout, " %s (%d) (no port)\n", obn->name, N1->graph);
if (N1->graph == Circuit1->file)
correspond[obn->node] = -1;
}
}
}
if (found == 1) Fprintf(stdout, "\n");
}
/* Run cleanuppins on circuit 1 */
if (needclean1)
CleanupPins(tc1->name, tc1->file);
+1 -1
View File
@@ -302,7 +302,7 @@ int GetNextLineNoNewline(char *delimiter)
{
char *newbuf;
int testc;
int nested = 0;
static int nested = 0;
int llen;
if (feof(infile)) return -1;
+10 -2
View File
@@ -534,7 +534,12 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
if ((EndParseFile()) && (nexttok == NULL)) break;
if (nexttok == NULL) break;
if (nexttok[0] == '*') SkipNewLine(NULL);
/* Handle comment lines. Note that some variants of CDL format
* use "*." for information that is transparent to SPICE simulators.
* Handle "*.GLOBAL" entries. All others are ignored.
*/
if ((nexttok[0] == '*') && (!matchnocase(nexttok, "*.GLOBAL")))
SkipNewLine(NULL);
else if (matchnocase(nexttok, ".SUBCKT")) {
SpiceTokNoNewline();
@@ -822,7 +827,10 @@ skip_ends:
// Handle some commonly-used cards
else if (matchnocase(nexttok, ".GLOBAL")) {
/* .GLOBAL and *.GLOBAL. Note that *.GLOBAL is excepted from comment-line
* handling, above, so any line starting with '*' is "*.GLOBAL".
*/
else if (matchnocase(nexttok, ".GLOBAL") || (nexttok[0] == '*')) {
while (nexttok != NULL) {
int numnodes = 0;
SpiceTokNoNewline();
+7 -5
View File
@@ -2614,13 +2614,15 @@ nextinst:
if (scan == NULL) {
char localnet[MAX_STR_LEN];
/* Assume an implicit unconnected pin */
sprintf(localnet, "_noconnect_%d_", localcount++);
Node(localnet);
join(localnet, obptr->name);
Fprintf(stdout,
/* Assume an implicit unconnected pin, unless there are no pins */
if (strcmp(obpinname, "(no pins)")) {
sprintf(localnet, "_noconnect_%d_", localcount++);
Node(localnet);
join(localnet, obptr->name);
Fprintf(stdout,
"Note: Implicit pin %s in instance %s of %s in cell %s\n",
obpinname, locinst, modulename, CurrentCell->name);
}
}
else if (GetBus(scan->net, &wb) == 0) {
char *bptr2;
+12
View File
@@ -477,6 +477,18 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
set cell2 [lindex $clist2 $cidx]
}
# The "noflat" list is non-file-specific, so run on each file.
foreach cell $noflat {
set cidx [lsearch -regexp $clist1 ^$cell$]
if {$cidx >= 0} {
netgen::flatten prohibit "$fnum1 $cell"
}
set cidx [lsearch -regexp $clist2 ^$cell$]
if {$cidx >= 0} {
netgen::flatten prohibit "$fnum2 $cell"
}
}
netgen::compare assign "$fnum1 $cell1" "$fnum2 $cell2"
if {$setupfile == ""} {
+12 -8
View File
@@ -1021,7 +1021,7 @@ _netgen_flatten(ClientData clientData,
{
char *repstr, *file;
int result, llen, filenum;
struct nlist *tp, *tp2;
struct nlist *tp, *tp2, *tptop;
if ((objc < 2) || (objc > 4)) {
Tcl_WrongNumArgs(interp, 1, objv, "?class? valid_cellname");
@@ -1035,7 +1035,7 @@ _netgen_flatten(ClientData clientData,
if (objc >= 3) {
char *argv = Tcl_GetString(objv[1]);
if (!strcmp(argv, "class")) {
tp = GetTopCell(filenum);
tptop = GetTopCell(filenum);
if (objc == 4) {
int numflat;
@@ -1046,7 +1046,7 @@ _netgen_flatten(ClientData clientData,
}
else {
Printf("Flattening instances of %s in cell %s within file %s\n",
repstr, tp2->name, tp->name);
repstr, tp2->name, tptop->name);
numflat = flattenInstancesOf(tp2->name, filenum, repstr);
if (numflat == 0) {
Tcl_SetResult(interp, "No instances found to flatten.", NULL);
@@ -1055,15 +1055,19 @@ _netgen_flatten(ClientData clientData,
}
}
else {
Printf("Flattening instances of %s in file %s\n", repstr, tp->name);
Printf("Flattening instances of %s in file %s\n", repstr, tptop->name);
FlattenInstancesOf(repstr, filenum);
}
}
else if (!strcmp(argv, "prohibit") || !strcmp(argv, "deny")) {
tp = GetTopCell(filenum);
Printf("Will not flatten instances of %s in file %s\n", repstr, tp->name);
/* Mark cell as placeholder so it will not be flattened */
tp->flags |= CELL_PLACEHOLDER;
tptop = GetTopCell(filenum);
if (tp == NULL)
Printf("Error: Cell %s does not exist.\n", repstr);
else {
Printf("Will not flatten instances of %s in file %s\n", repstr, tptop->name);
/* Mark cell as placeholder so it will not be flattened */
tp->flags |= CELL_PLACEHOLDER;
}
}
else {
Tcl_WrongNumArgs(interp, 1, objv, "class valid_cellname");