From 0bee21ccc87a7606a796c4941eecb93030cd2690 Mon Sep 17 00:00:00 2001 From: "R. Timothy Edwards" Date: Tue, 26 Aug 2025 17:47:46 -0400 Subject: [PATCH] Corrected an issue in which a property error in a subcell would not be reported at the end if there was a port error. This is important because port errors often resolve themselves, but the cell should not be reported clean if the port errors resolved but it had property errors. Also: Added a method to derive area and/or perimeter properties from length and width, so that capacitors can be combined in parallel without regard to which dimension is width and which is length. This feature has only been lightly tested. --- VERSION | 2 +- base/netcmp.c | 176 ++++++++++++++++++++++++++++++++++++++++++++ base/netcmp.h | 2 + tcltk/netgen.tcl.in | 3 + tcltk/tclnetgen.c | 69 ++++++++++++++--- 5 files changed, 242 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index d898cef..6b03f2a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.297 +1.5.298 diff --git a/base/netcmp.c b/base/netcmp.c index dfa3359..6fe8af5 100644 --- a/base/netcmp.c +++ b/base/netcmp.c @@ -8481,6 +8481,182 @@ int EquivalentElement(char *name, struct nlist *circuit, struct objlist **retobj return retval; } +/*------------------------------------------------------*/ +/* Structure and definitins used by derivedprops() */ +/*------------------------------------------------------*/ + +enum DerivedType {area_type, perimeter_type}; + +typedef struct _derivedpropdata { + struct nlist *cell; + int fnum; + char *pwidth; + char *plength; + enum DerivedType type; +} DerivedPropData; + +/*------------------------------------------------------*/ +/* derivedprops --- Callback function for a recursive */ +/* search over all cells with a pointer clientdata. */ +/* The pointer is a DerivedPropData structure that */ +/* contains the information needed to determine how to */ +/* generate an "area" or "perimeter" property based on */ +/* the device length and width. */ +/*------------------------------------------------------*/ + +struct nlist *derivedprops(struct hashlist *p, void *clientdata) +{ + struct nlist *ptr; + struct objlist *ob; + struct valuelist *vl, *newvlist; + struct nlist *tc; + struct property *prop; + int i; + double valuew, valuel, valuea = 0.0, valuep = 0.0; + int haswidth = FALSE, haslength = FALSE; + int hasarea = FALSE, hasperimeter = FALSE; + + DerivedPropData *dpd = (DerivedPropData *)clientdata; + + tc = dpd->cell; + ptr = (struct nlist *)(p->ptr); + if (ptr->file != tc->file) return NULL; + + /* Search all instances in the cell for properties, find those matching + * the cell class to be modified, and create a new derived property for + * area or perimeter for that instance. + */ + + for (ob = ptr->cell; ob; ob = ob->next) { + if (ob->type == PROPERTY) { + if ((*matchfunc)(ob->model.class, tc->name)) { + for (i = 0;; i++) { + vl = &(ob->instance.props[i]); + if (vl->type == PROP_ENDLIST) break; + prop = (struct property *)HashLookup(vl->key, &(tc->propdict)); + if (prop != NULL) { + if ((*matchfunc)(vl->key, dpd->pwidth)) { + haswidth = TRUE; + if (vl->type == PROP_DOUBLE) + valuew = vl->value.dval; + else if (vl->type == PROP_INTEGER) + valuew = (double)vl->value.ival; + else + haswidth = FALSE; + } + else if ((*matchfunc)(vl->key, dpd->plength)) { + haslength = TRUE; + if (vl->type == PROP_DOUBLE) + valuel = vl->value.dval; + else if (vl->type == PROP_INTEGER) + valuel = (double)vl->value.ival; + else + haslength = FALSE; + } + else if ((dpd->type == area_type) + && ((*matchfunc)(vl->key, "area"))) { + hasarea = TRUE; + } + else if ((dpd->type == perimeter_type) + && ((*matchfunc)(vl->key, "perimeter"))) { + hasperimeter = TRUE; + } + } + } + if (haslength && haswidth) { + + /* Once the property names for device width and length have + * been found, and the values recorded, add the area or + * perimeter value to the property list for the instance, + * unless the instance already has the property. + */ + newvlist = (struct valuelist *)CALLOC(i + 1, sizeof(struct valuelist)); + vl = &newvlist[i]; + vl->key = NULL; + vl->type = PROP_ENDLIST; + vl->value.ival = 0; + + vl = &newvlist[--i]; + if ((dpd->type == area_type) && (!hasarea)) { + valuea = valuew * valuel; + vl->key = strsave("area"); + vl->type = PROP_DOUBLE; + vl->value.dval = valuea; + + } else if ((dpd->type == perimeter_type) && (!hasperimeter)) { + valuep = 2 * (valuew + valuel); + vl->key = strsave("perimeter"); + vl->type = PROP_DOUBLE; + vl->value.dval = valuep; + } + for (--i; i >= 0; i--) { + vl = &newvlist[i]; + vl->key = ob->instance.props[i].key; + vl->type = ob->instance.props[i].type; + vl->value = ob->instance.props[i].value; + } + FREE(ob->instance.props); + ob->instance.props = newvlist; + } + } + } + } +} + +/*------------------------------------------------------*/ +/* Create a new "area" or "perimeter" property in all */ +/* instances of a given device, based on the specified */ +/* names for the width and length parameters. */ +/*------------------------------------------------------*/ + +void +DeriveProperty(struct nlist *tc, int fnum, char *pwidth, char *plength, + enum DerivedType type) +{ + DerivedPropData dpd; + + dpd.pwidth = pwidth; + dpd.plength = plength; + dpd.fnum = fnum; + dpd.cell = tc; + dpd.type = type; + + /* Create the new derived property in the cell. */ + if (type == area_type) + PropertyDouble(tc->name, fnum, "area", 0.01, 0.0); + else if (type == perimeter_type) + PropertyDouble(tc->name, fnum, "perimeter", 0.01, 0.0); + else + return; + + /* Find all instances of the cell and add the derived property */ + RecurseCellHashTable2(derivedprops, (void *)(&dpd)); +} + +/*------------------------------------------------------*/ +/* Create a new "area" property in all instances of a */ +/* given device, based on the specified width and */ +/* length parameter names. */ +/*------------------------------------------------------*/ + +void +DeriveAreaProperty(struct nlist *tp, int fnum, char *pwidth, char *plength) +{ + DeriveProperty(tp, fnum, pwidth, plength, area_type); +} + +/*------------------------------------------------------*/ +/* Create a new "perimeter" property in all instances */ +/* of a given device, based on the specified width and */ +/* length parameter names. */ +/*------------------------------------------------------*/ + +void +DerivePerimeterProperty(struct nlist *tp, int fnum, char *pwidth, char *plength) +{ + DeriveProperty(tp, fnum, pwidth, plength, perimeter_type); +} + /*------------------------------------------------------*/ /* Flatten the two cells at the top of the compare */ /* queue. */ diff --git a/base/netcmp.h b/base/netcmp.h index 6d90178..a0f646a 100644 --- a/base/netcmp.h +++ b/base/netcmp.h @@ -70,6 +70,8 @@ extern int remove_group_tags(struct objlist *ob); #ifdef TCL_NETGEN extern int EquivalentNode(); extern int EquivalentElement(); +extern void DeriveAreaProperty(); +extern void DerivePerimeterProperty(); extern void enable_interrupt(); extern void disable_interrupt(); diff --git a/tcltk/netgen.tcl.in b/tcltk/netgen.tcl.in index 2d5c13c..c413f44 100644 --- a/tcltk/netgen.tcl.in +++ b/tcltk/netgen.tcl.in @@ -594,6 +594,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} { lappend properr [lindex $endval 0] } elseif {$uresult == -2} { ;# unmatched pins set doCheckFlatten 1 + } elseif {$uresult == -4} { ;# unmatched pins and properties + lappend properr [lindex $endval 0] + set doCheckFlatten 1 } } else { # not equivalent diff --git a/tcltk/tclnetgen.c b/tcltk/tclnetgen.c index 5071a02..04274f6 100644 --- a/tcltk/tclnetgen.c +++ b/tcltk/tclnetgen.c @@ -2572,6 +2572,7 @@ _netcmp_run(ClientData clientData, /* 0: not verified */ /* -1: no elements or nodes */ /* -3: verified with property error */ +/* -4: verified with property and port errors */ /* equiv option */ /* -2: pin mismatch */ /* */ @@ -2673,8 +2674,12 @@ _netcmp_verify(ClientData clientData, else if (automorphisms == -2) { if (index == EQUIV_IDX) Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); - else if (index == UNIQUE_IDX) - Tcl_SetObjResult(interp, Tcl_NewIntObj(-2)); + else if (index == UNIQUE_IDX) { + if (PropertyErrorDetected == 0) + Tcl_SetObjResult(interp, Tcl_NewIntObj(-2)); + else + Tcl_SetObjResult(interp, Tcl_NewIntObj(-4)); + } else if (index > 0) Fprintf(stdout, "Circuits match uniquely with port errors.\n"); } @@ -3428,13 +3433,21 @@ _netcmp_property(ClientData clientData, double dval; int ival, argstart; + char *topoptions[] = { + "default", "series", "serial", "parallel", "topology", NULL + }; + enum TopOptionIdx { + TOP_DEFAULT_IDX, TOP_SERIES_IDX, TOP_SERIAL_IDX, TOP_PARALLEL_IDX, + TOP_TOPOLOGY_IDX + }; + char *options[] = { "add", "create", "remove", "delete", "tolerance", "merge", "serial", - "series", "parallel", "associate", "topology", NULL + "series", "parallel", "associate", "derive", NULL }; enum OptionIdx { ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX, - SERIAL_IDX, SERIES_IDX, PARALLEL_IDX, ASSOCIATE_IDX, TOPOLOGY_IDX + SERIAL_IDX, SERIES_IDX, PARALLEL_IDX, ASSOCIATE_IDX, DERIVE_IDX }; int result, index, idx2; @@ -3469,6 +3482,14 @@ _netcmp_property(ClientData clientData, COMB_NONE_IDX, COMB_PAR_IDX, COMB_ADD_IDX, COMB_CRITICAL_IDX }; + char *deriveoptions[] = { + "area", "perimeter", NULL + }; + + enum DeriveOptionIdx { + AREA_IDX, PERIMETER_IDX + }; + char *yesno[] = { "on", "yes", "true", "enable", "allow", "off", "no", "false", "disable", "prohibit", NULL @@ -3483,8 +3504,13 @@ _netcmp_property(ClientData clientData, "strict", "relaxed", NULL }; + /* Don't need to check return value */ + index = -1; + Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)topoptions, + "option", 0, &index); + /* Check for special command "property default" */ - if ((objc == 2) && (!strcmp(Tcl_GetString(objv[1]), "default"))) { + if ((objc == 2) && (index == TOP_DEFAULT_IDX)) { /* For each FET device, do "merge {w add_critical}" and */ /* "remove as ad ps pd". This allows parallel devices */ @@ -3531,7 +3557,7 @@ _netcmp_property(ClientData clientData, } return TCL_OK; } - else if ((objc == 3) && (!strcmp(Tcl_GetString(objv[1]), "parallel"))) { + else if ((objc == 3) && (index == TOP_PARALLEL_IDX)) { if (!strcmp(Tcl_GetString(objv[2]), "none")) { GlobalParallelNone = TRUE; SetParallelCombine(FALSE); @@ -3553,8 +3579,7 @@ _netcmp_property(ClientData clientData, } return TCL_OK; } - else if ((objc == 3) && ((!strcmp(Tcl_GetString(objv[1]), "series")) || - (!strcmp(Tcl_GetString(objv[1]), "serial")))) { + else if ((objc == 3) && ((index == TOP_SERIES_IDX) || (index == TOP_SERIAL_IDX))) { if (!strcmp(Tcl_GetString(objv[2]), "none")) { SetSeriesCombine(FALSE); } @@ -3568,7 +3593,7 @@ _netcmp_property(ClientData clientData, } return TCL_OK; } - else if ((objc > 1) && (!strcmp(Tcl_GetString(objv[1]), "topology"))) { + else if ((objc > 1) && (index == TOP_TOPOLOGY_IDX)) { if (objc == 2) { if (ExactTopology) Tcl_SetResult(interp, "Strict topology property matching.", @@ -4014,6 +4039,32 @@ _netcmp_property(ClientData clientData, } break; + case DERIVE_IDX: + /* Create a derived property. For now, this just creates + * "area" or "perimeter" properties. There may not (?) + * be enough cause to make other ones. Unlike other + * "property" options, this option causes the whole circuit + * database to be searched for the device in question, and + * the new property is added. + */ + if (objc < 6) { + Tcl_WrongNumArgs(interp, 2, objv, "{area|perimeter width length}"); + return TCL_ERROR; + } + result = Tcl_GetIndexFromObj(interp, objv[3], + (CONST84 char **)deriveoptions, + "area|perimeter", 0, &idx2); + if (result != TCL_OK) return result; + switch (idx2) { + case AREA_IDX: + DeriveAreaProperty(tp, fnum, Tcl_GetString(objv[4]), + Tcl_GetString(objv[5])); + break; + case PERIMETER_IDX: + DerivePerimeterProperty(tp, fnum, Tcl_GetString(objv[4]), + Tcl_GetString(objv[5])); + break; + } } } return TCL_OK;