Compare commits

...
14 Commits
Author SHA1 Message Date
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
Tim Edwards 33fed391fd Merge branch 'master' into netgen-1.5 2025-09-01 02:00:02 -04:00
R. Timothy Edwards e84700a607 Added a NULL check at one point in the SPICE read routine that
prevents a segfault under some condition (not fully investigated)
involving .include files.  Appears to resolve the problem without
any unintended consequences.
2025-08-31 16:52:35 -04:00
Tim Edwards 5f5248b3d0 Merge branch 'master' into netgen-1.5 2025-08-27 02:00:03 -04:00
R. Timothy Edwards 0bee21ccc8 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.
2025-08-26 17:47:46 -04:00
Tim Edwards 80f9263004 Merge branch 'master' into netgen-1.5 2025-08-26 02:00:02 -04:00
R. Timothy Edwards c269f1de89 Corrected an unexpected corner-case error in which if a newline in
a spice netlist falls exactly on the last non-null position of the
input buffer after the buffer has been expanded to accept more
input data, then the next line gets read in automatically, and
the newline gets treated as whitespace and not a newline.
2025-08-25 10:31:19 -04:00
Tim Edwards edb50746cb Merge branch 'master' into netgen-1.5 2025-08-19 02:00:02 -04:00
R. Timothy Edwards 4443826f9e Corrected a place in netcmp.c where a new instance net connection
is created without setting the cell name or instance name.  That
can cause a crash condition when attempting to locate the instance
from the net record.
2025-08-18 10:37:36 -04:00
Tim Edwards f2368ca223 Merge branch 'master' into netgen-1.5 2025-05-18 02:00:02 -04:00
R. Timothy Edwards a60dac6124 Modified the primary SPICE token reading routine so that the call
to strdtok() can differentiate between reading verilog and reading
SPICE.  Otherwise, SPICE containing the (dubious) syntax of using
backslashes in names will get treated as a verilog name with
verilog backslash notation, with generally undesirable results.
When called from the SPICE reading routine, backslashes are
treated as-is and not as verilog notation.
2025-05-17 20:29:38 -04:00
8 changed files with 364 additions and 44 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.294
1.5.301
+267 -29
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
@@ -7530,6 +7556,12 @@ struct nlist *addproxies(struct hashlist *p, void *clientdata)
else {
lob = ob;
ob->type = i++;
if (ob->model.class == NULL) {
ob->model.class = strsave(tc->name);
}
if (ob->instance.name == NULL) {
ob->instance.name = strsave(firstpin->instance.name);
}
ob = ob->next;
}
tob = tob->next;
@@ -7595,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;
@@ -7627,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;
@@ -7845,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'))
@@ -7967,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;
@@ -7997,10 +8029,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
}
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;
@@ -8263,6 +8293,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);
@@ -8475,6 +8537,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. */
+2
View File
@@ -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();
+19 -3
View File
@@ -329,6 +329,11 @@ int GetNextLineNoNewline(char *delimiter)
llen = strlen(line);
}
while (llen == linesize - 1) {
/* Note that in the rare case where a newline is in the last buffer
* position, we're done.
*/
if (*(line + llen - 1) == '\n') break;
newbuf = (char *)MALLOC(linesize + 501);
strcpy(newbuf, line);
FREE(line);
@@ -599,7 +604,7 @@ void SpiceTokNoNewline(void)
{
int contline;
if ((nexttok = strdtok(NULL, WHITESPACE_DELIMITER, NULL)) != NULL) return;
if ((nexttok = strdtok0(NULL, WHITESPACE_DELIMITER, NULL, FALSE)) != NULL) return;
while (nexttok == NULL) {
contline = getc(infile);
@@ -701,7 +706,7 @@ void SpiceSkipNewLine(void)
/* the boundary between two-character and one-character delimiters. */
/*----------------------------------------------------------------------*/
char *strdtok(char *pstring, char *delim1, char *delim2)
char *strdtok0(char *pstring, char *delim1, char *delim2, char isverilog)
{
static char *stoken = NULL;
static char *sstring = NULL;
@@ -746,7 +751,7 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
/* should know whether it is parsing SPICE or verilog and handle the syntax */
/* accordingly (needs to be done). */
if (*s == '\\') {
if (isverilog && (*s == '\\')) {
s++;
while (*s != '\0') {
if ((*s == ' ') || ((*s == '\\') && (*(s + 1) == '\0'))) {
@@ -817,6 +822,17 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
return sstring;
}
/*----------------------------------------------------------------------*/
/* strdtok() is the original string tokenizer. It calls strdtok0() */
/* with isverilog=TRUE, so that tokens are parsed as (potentially) */
/* verilog names, which includes verilog backslash notation. */
/*----------------------------------------------------------------------*/
char *strdtok(char *pstring, char *delim1, char *delim2)
{
return strdtok0(pstring, delim1, delim2, TRUE);
}
/*----------------------------------------------------------------------*/
void InputParseError(FILE *f)
+1
View File
@@ -36,6 +36,7 @@ extern struct hashdict *definitions;
extern char *nexttok;
#define SKIPTO(a) do {SkipTok(NULL);} while (!match(nexttok,a))
extern char *strdtok0(char *pstring, char *delim1, char *delim2, char isverilog);
extern char *strdtok(char *pstring, char *delim1, char *delim2);
extern char *GetLineAtTok();
extern void SkipTok(char *delimiter);
+11 -2
View File
@@ -532,8 +532,14 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
SkipTok(NULL); /* get the next token */
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();
@@ -821,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();
+3
View File
@@ -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
+60 -9
View File
@@ -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;