Compare commits

...
9 Commits
Author SHA1 Message Date
Tim Edwards d111fa0a3b Merge branch 'master' into netgen-1.5 2023-03-30 02:00:02 -04:00
Tim Edwards 66317c9848 Corrected an error in order-of-precedence of arithmetic operators,
ensuring that, e.g., in (a)*b+c, (a)*b gets evaluated before b+c.
2023-03-29 19:45:39 -04:00
Tim Edwards eeb3c0e5c6 Added support for simple forms of for() loops in generate blocks.
This is done by treating the loop variable as a temporary parameter
that is valid only inside the loop, and changing the parameter
value on each loop iteration.  The file stream position is used
to iterate the loop with calls to fseek() and ftell(), so that the
input tokenizer continues to work within loops.
2023-03-29 16:17:37 -04:00
Tim Edwards 490f9f7dbc Added a missing check for using a verilog macro definition as an
array delimeter for an instance array in verilog.
2023-03-29 09:54:45 -04:00
Tim Edwards 178b172c06 Merge branch 'master' into netgen-1.5 2023-03-08 02:00:03 -05:00
Tim Edwards 40cf82c2cb Slightly modified the fix from the last commit to allow an empty
string for the setup file to be the "trivial default" previously
used in case of the setup file not being found.  Put a newline
around the setup file messages so that they stand out from the
rest of the initial output information.
2023-03-07 09:00:39 -05:00
Tim Edwards 1ac2b592fb Changed what was a not-very-well thought out behavior: On being
passed an invalid setup file, the netgen "lvs" script uses a
trivial default setup and issues no error or warning.  Replaced
this behavior with an error message and a hard stop.
2023-03-07 08:53:06 -05:00
Tim Edwards 7870538ec9 Merge branch 'master' into netgen-1.5 2023-03-07 02:00:02 -05:00
Tim Edwards e12883037c Modified code from EquivalenceClasses() that forces the two cells
to have unique class hashes.  This has the problem that it prevents
comparing N-to-1 cells because declaring X->X1 as equivalent breaks
the original name equivalence of X->X.  The new implementation adds
the switch "-unique" to preserve the original behavior.  Otherwise,
the class hashes are made the same as the 2nd cell passed to the
command, and it is the responsibility of the person running LVS to
ensure that this is done in the correct direction.
2023-03-06 09:36:35 -05:00
8 changed files with 405 additions and 40 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.248
1.5.251
+16 -10
View File
@@ -7068,12 +7068,15 @@ int IgnoreClass(char *name, int file, unsigned char type)
/* If file1 and file2 are -1, then these are names to */
/* be checked as netcmp works through the hierarchy. */
/* Otherwise, look up the structure for each file and */
/* set the classhash of the second to that of the first */
/* set the classhash of the second to that of the */
/* first. If "dounique" is 1, then give the two cells */
/* their own class hash; they will not be compared */
/* any other cells in either netlist. */
/* */
/* Return 1 on success, 0 on failure */
/*------------------------------------------------------*/
int EquivalenceClasses(char *name1, int file1, char *name2, int file2)
int EquivalenceClasses(char *name1, int file1, char *name2, int file2, int dounique)
{
char *class1, *class2;
struct Correspond *newc;
@@ -7098,16 +7101,19 @@ int EquivalenceClasses(char *name1, int file1, char *name2, int file2)
if (tp->flags & CELL_DUPLICATE)
reverse = 1;
/* Do a cross-check for each name in the other netlist. If */
/* conflicting names exist, then alter the classhash to make it */
/* unique. In the case of duplicate cells, don't do this. */
if (dounique) {
if (!(tp->flags & CELL_DUPLICATE) && !(tp2->flags & CELL_DUPLICATE) &&
/* Do a cross-check for each name in the other netlist. If */
/* conflicting names exist, then alter the classhash to make */
/* it unique. In the case of duplicate cells, don't do this. */
if (!(tp->flags & CELL_DUPLICATE) && !(tp2->flags & CELL_DUPLICATE) &&
!(*matchfunc)(name1, name2)) {
tpx = LookupCellFile(name1, file2);
if (tpx != NULL) need_new_seed = 1;
tpx = LookupCellFile(name2, file1);
if (tpx != NULL) need_new_seed = 1;
tpx = LookupCellFile(name1, file2);
if (tpx != NULL) need_new_seed = 1;
tpx = LookupCellFile(name2, file1);
if (tpx != NULL) need_new_seed = 1;
}
}
/* Now make the classhash values the same so that these cells */
+2 -1
View File
@@ -38,7 +38,8 @@ extern int PermuteSetup(char *model, int filenum, char *pin1, char *pin2);
extern int PermuteForget(char *model, int filenum, char *pin1, char *pin2);
extern int EquivalenceElements(char *name1, int file1, char *name2, int file2);
extern int EquivalenceNodes(char *name1, int file1, char *name2, int file2);
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2);
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2,
int dounique);
extern int IgnoreClass(char *name, int file, unsigned char type);
extern int MatchPins(struct nlist *tp1, struct nlist *tp2, int dolist);
extern int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run,
+15 -1
View File
@@ -183,7 +183,6 @@ void CloseFile(char *filename)
fclose(outfile);
}
/* STUFF TO READ INPUT FILES */
static char *line = NULL; /* actual line read in */
@@ -207,6 +206,21 @@ struct hashdict *definitions = (struct hashdict *)NULL;
#define WHITESPACE_DELIMITER " \t\n\r"
/*----------------------------------------------------------------------*/
/* Seek and Tell on infile stream, for use with handling generate */
/* loops in verilog. */
/*----------------------------------------------------------------------*/
void SeekFile(long offset)
{
fseek(infile, offset, SEEK_SET);
}
long TellFile()
{
return ftell(infile);
}
/*----------------------------------------------------------------------*/
/* TrimQuoted() --- */
/* Remove spaces from inside single- or double-quoted strings. */
+2
View File
@@ -47,5 +47,7 @@ extern void InputParseError(FILE *f);
extern int OpenParseFile(char *name, int fnum);
extern int EndParseFile(void);
extern int CloseParseFile(void);
extern void SeekFile(long offset); /* handles verilog 'for' loops */
extern long TellFile(); /* handles verilog 'for' loops */
#endif /* _NETFILE_H */
+352 -11
View File
@@ -42,6 +42,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <stdlib.h> /* for calloc(), free(), getenv() */
#include <ctype.h> /* for isalnum() */
#ifndef IBMPC
#include <sys/types.h> /* for getpwnam() tilde expansion */
#include <pwd.h>
@@ -63,6 +64,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#define VLOG_EQUATION_DELIMITERS "X///**/#((**)X,;:(){}[]=+-*/"
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
#define VLOG_INTEGER_DELIMITERS "X///**/X;[]"
// Used by portelement structure "flags" record.
#define PORT_NOT_FOUND 0
@@ -83,6 +85,16 @@ struct bus {
int end;
};
// Global storage for a 'for' loop
struct _loop {
char *loopvar;
int start;
int end;
long filepos;
};
struct _loop loop;
// Free a bus structure in the hash table during cleanup
int freebus (struct hashlist *p)
@@ -244,7 +256,16 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
for (texp = start; texp; texp = texp->next) {
if ((texp->last != NULL) && (texp->next != NULL) && (texp->oper != '\0')) {
if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
/* Watch for (a)*b+c or a+b*(c); multiplies must be solved first */
if ((texp->last->last != NULL) && ((texp->last->last->oper == '*')
|| (texp->last->last->oper == '/'))) {
/* Do nothing */
}
else if ((texp->next->next != NULL) && ((texp->next->next->oper == '*')
|| (texp->next->next->oper == '/'))) {
/* Do nothing */
}
else if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
if (texp->oper == '-') {
/* Subtract */
texp->last->value -= texp->next->value;
@@ -290,6 +311,7 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
}
}
}
}
/* If only one numerical item remains, then place it in valptr and return 1 */
@@ -311,6 +333,134 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
return 0;
}
//-------------------------------------------------------------------------
// Parse an expression which must resolve to a single integer.
// If it does, return "1" and put the integer in the return
// pointer "iptr". If not, return "0" and iptr is undefined.
// The expression may use parameters, standard arithmetic,
// and parenthetic grouping. This routine does not tokenize input
// but assumes that the entire expression is in the string "expr".
//-------------------------------------------------------------------------
int ParseIntegerExpression(char *expr, int *iptr)
{
int result, value;
char *sptr, *cptr, savec;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
stack = NULL;
savec = '\0';
sptr = expr;
result = 1;
while (sptr && (*sptr != '\0')) {
// Move sptr to first non-space character
while (isspace(*sptr)) sptr++;
// Tokenize. Look ahead to next delimeter and truncate string there.
cptr = sptr + 1;
if (isalnum(*sptr) || (*sptr == '_') || (*sptr == '$')) {
while (*cptr != '\0') {
if (isalnum(*cptr)) cptr++;
else if ((*cptr == '_') || (*cptr == '$')) cptr++;
else break;
}
}
savec = *cptr;
*cptr = '\0';
if (match(sptr, "+") || match(sptr, "-")
|| match(sptr, "*") || match(sptr, "/")
|| match(sptr, "(") || match(sptr, ")")) {
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
newexp->oper = *sptr;
newexp->value = 0;
newexp->next = NULL;
newexp->last = stack;
if (stack) stack->next = newexp;
stack = newexp;
sptr = cptr;
*cptr = savec;
continue;
}
if ((result = sscanf(sptr, "%d", &value)) != 1) {
// Is name in the parameter list?
kl = (struct property *)HashLookup(nexttok, &verilogparams);
if (kl == NULL) {
Printf("Value %s in expression is not a number or a parameter.\n",
sptr);
value = 0;
break;
}
else {
if (kl->type == PROP_STRING) {
result = sscanf(kl->pdefault.string, "%d", &value);
if (result != 1) {
Printf("Parameter %s has value %s that cannot be parsed"
" as an integer.\n",
nexttok, kl->pdefault.string);
value = 0;
break;
}
}
else if (kl->type == PROP_INTEGER) {
value = kl->pdefault.ival;
}
else if (kl->type == PROP_DOUBLE) {
value = (int)kl->pdefault.dval;
if ((double)value != kl->pdefault.dval) {
Printf("Parameter %s has value %g that cannot be parsed"
" as an integer.\n",
nexttok, kl->pdefault.dval);
value = 0;
break;
}
}
else {
Printf("Parameter %s has unknown type; don't know how"
" to parse.\n", nexttok);
value = 0;
break;
}
}
}
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
newexp->oper = '\0';
newexp->value = value;
newexp->next = NULL;
newexp->last = stack;
if (stack) stack->next = newexp;
stack = newexp;
/* Move to next token */
sptr = cptr;
*cptr = savec;
}
if (result != 0) {
if (stack == NULL) {
Printf("Empty array found.\n");
result = 0;
}
else if (EvalExpr(&stack, iptr) != 1) {
Printf("Bad expression found in array.\n");
result = 0;
}
}
/* In case of error, stack may need cleaning up */
while (stack != NULL) {
newexp = stack;
stack = stack->last;
FREE(newexp);
}
return result;
}
//-------------------------------------------------------------------------
// Get bus indexes from the notation name[a:b]. If there is only "name"
// then look up the name in the bus hash list and return the index bounds.
@@ -323,7 +473,6 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
int GetBusTok(struct bus *wb)
{
int result, start, end, value;
char oper;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
@@ -338,7 +487,6 @@ int GetBusTok(struct bus *wb)
if (match(nexttok, "[")) {
start = end = -1;
oper = '\0';
while (nexttok) {
SkipTokComments(VLOG_EQUATION_DELIMITERS);
if (match(nexttok, "]")) {
@@ -467,13 +615,16 @@ int GetBusTok(struct bus *wb)
//--------------------------------------------------------------------
// GetBus() is similar to GetBusTok() (see above), but it parses from
// a string instead of the input tokenizer.
// a string instead of the input tokenizer, and expressions are not
// allowed.
//--------------------------------------------------------------------
int GetBus(char *astr, struct bus *wb)
{
char *colonptr, *brackstart, *brackend, *sigend, sdelim, *aastr;
int result, start, end;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
if (wb == NULL) return 0;
else {
@@ -531,7 +682,7 @@ int GetBus(char *astr, struct bus *wb)
*brackend = '\0';
colonptr = strvchr(aastr, ':');
if (colonptr) *colonptr = '\0';
result = sscanf(brackstart + 1, "%d", &start);
result = ParseIntegerExpression(brackstart + 1, &start);
if (colonptr) *colonptr = ':';
if (result != 1) {
Printf("Badly formed array notation \"%s\"\n", astr);
@@ -539,7 +690,7 @@ int GetBus(char *astr, struct bus *wb)
return 1;
}
if (colonptr)
result = sscanf(colonptr + 1, "%d", &end);
result = ParseIntegerExpression(colonptr + 1, &end);
else {
result = 1;
end = start; // Single bit
@@ -876,6 +1027,8 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
in_module = (char)0;
in_param = (char)0;
loop.loopvar = NULL;
while (!EndParseFile()) {
SkipTokComments(VLOG_DELIMITERS); /* get the next token */
@@ -1416,6 +1569,145 @@ skip_endmodule:
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
continue;
}
else if (match(nexttok, "end")) {
/* Handle a 'for' loop */
if (loop.loopvar != NULL) {
int loopval;
struct property *klr;
klr = (struct property *)HashLookup(loop.loopvar, &verilogparams);
loopval = klr->pdefault.ival;
if (loopval < loop.end) loopval++;
else if (loopval > loop.end) loopval--;
SeekFile(loop.filepos);
if (loopval == loop.end)
{
HashDelete(loop.loopvar, &verilogparams);
FREE(klr);
FREE(loop.loopvar);
loop.loopvar = NULL;
}
else
klr->pdefault.ival = loopval;
}
continue;
}
else if (match(nexttok, "begin") || match(nexttok, "generate")) {
/* 'generate' section or 'for' loop start is ignored */
continue;
}
else if (match(nexttok, "generate") || match(nexttok, "endgenerate")) {
/* 'generate' section is ignored */
continue;
}
else if (match(nexttok, "genvar")) {
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
continue;
}
else if (match(nexttok, "for")) {
char limittype = '\0';
char *paramkey = NULL;
struct property *kl = NULL, *klr;
/* Parse out the loop variable and count. Set the loop
* variable as a parameter. Find the beginning of the
* loop block and record the file position so that the
* block can be re-parsed for each loop iteration.
*/
SkipTokNoNewline(VLOG_DELIMITERS);
if ((nexttok == NULL) || !match(nexttok, "(")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
break;
}
SkipTokNoNewline(VLOG_DELIMITERS);
/* Next token must be the loop variable */
if (nexttok == NULL) break;
paramkey = strsave(nexttok);
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, "=")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
break;
}
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
if (ParseIntegerExpression(nexttok, &ival) == 0) {
FREE(paramkey);
break;
}
kl = NewProperty();
kl->type = PROP_INTEGER;
kl->slop.ival = 0;
kl->pdefault.ival = ival;
kl->key = paramkey;
kl->idx = 0;
kl->merge = MERGE_NONE;
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, ";")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Assuming a standard 'for' loop here */
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, paramkey)) {
Printf("Don't know how to parse this 'for' loop!\n");
FREE(paramkey);
FREE(kl);
break;
}
SkipTokNoNewline(VLOG_DELIMITERS);
if (strlen(nexttok) == 1) limittype = *nexttok;
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
if (ParseIntegerExpression(nexttok, &ival) == 0) {
FREE(kl);
FREE(paramkey);
break;
}
/* Loops will stop after the last value, so if value is > or <,
* then adjust the end value accordingly.
*/
if (limittype == '<')
ival--;
else if (limittype == '>')
ival++;
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, ";")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Assume a standard 'for' loop and skip to the block begin */
/* To do: Parse out the loop increment value. */
while (1) {
SkipTokNoNewline(VLOG_DELIMITERS);
if (EndParseFile()) break;
else if (match(nexttok, "begin")) break;
}
if (EndParseFile()) {
Printf("Badly formed 'for' loop: No begin/end block.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Save the loop variable, ending value, and file position */
loop.loopvar = paramkey;
loop.start = kl->pdefault.ival;
loop.end = ival;
loop.filepos = TellFile();
/* 'for' loop has been completely parsed, so save the loop variable */
HashPtrInstall(paramkey, kl, &verilogparams);
}
else if (match(nexttok, "wire") || match(nexttok, "assign")) { /* wire = node */
struct bus wb, wb2, *nb;
char nodename[MAX_STR_LEN], noderoot[MAX_STR_LEN];
@@ -1479,7 +1771,7 @@ skip_endmodule:
}
else { /* "assign" */
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
if (GetBus(nexttok, &wb) == 0) {
if (GetBusTok(&wb) == 0) {
char *aptr = strvchr(nexttok, '[');
if (aptr != NULL) {
*aptr = '\0';
@@ -1514,9 +1806,11 @@ skip_endmodule:
// "assign a = b" joins two nets.
// "assign a = {b, c, ...}" creates a bus from components.
// "assign" using any boolean arithmetic is not structural verilog.
// "assign a = {x{b}}" creates a bus by repeating a component.
if (nexttok && match(nexttok, "=")) {
char assignname[MAX_STR_LEN], assignroot[MAX_STR_LEN];
int multiplier = 1;
i = wb.start;
while (1) {
@@ -1525,6 +1819,10 @@ skip_endmodule:
if (match(nexttok, "{")) {
/* RHS is a bundle */
/* Make provisional multiplier active. NOTE: */
/* this is not going to properly handle complex */
/* multipliers like {2{a,b}}. Needs rework. */
if (multiplier < 0) multiplier = -multiplier;
continue;
}
else if (match(nexttok, "}")) {
@@ -1540,7 +1838,7 @@ skip_endmodule:
break;
}
else {
if (GetBus(nexttok, &wb2) == 0) {
if (GetBusTok(&wb2) == 0) {
char *aptr = strvchr(nexttok, '[');
j = wb2.start;
if (aptr != NULL) {
@@ -1556,6 +1854,18 @@ skip_endmodule:
else {
j = -1;
rhs = LookupObject(nexttok, CurrentCell);
/* Check if rhs starts with a signal multiplier */
if (rhs == NULL) {
if (ConvertStringToInteger(nexttok, &multiplier) == 1) {
/* Set multiplier to a negative value to
* indicate that it is provisional, waiting on
* signal specification to follow.
*/
multiplier = -multiplier;
continue;
}
}
}
if ((lhs == NULL) || (rhs == NULL)) {
if (rhs != NULL) {
@@ -1568,7 +1878,7 @@ skip_endmodule:
Printf("Improper expression is \"%s\".\n", nexttok);
break;
}
if (lhs != NULL) {
if ((lhs != NULL) || (multiplier < 0)) {
Printf("Improper assignment; right-hand side cannot "
"be parsed.\n");
if (i != -1)
@@ -1601,8 +1911,14 @@ skip_endmodule:
if (i == wb.end) break;
i += (wb.end > wb.start) ? 1 : -1;
if (j == wb2.end) break;
j += (wb2.end > wb2.start) ? 1 : -1;
if (j == wb2.end) {
if (multiplier <= 1)
break;
else
multiplier--;
}
else
j += (wb2.end > wb2.start) ? 1 : -1;
}
}
}
@@ -1810,6 +2126,31 @@ nextinst:
}
new_port->net = wire_bundle;
}
else if ((strchr(nexttok, '[') != NULL) &&
(strchr(nexttok, ']') == NULL)) {
/* If a bus expressions has whitespace, then treat it like
* a bundle, above, concatenating to the closing ']'.
*/
char *array_expr = (char *)MALLOC(1);
char *new_array_expr = NULL;
*array_expr = '\0';
/* Read to "]" */
while (nexttok) {
new_array_expr = (char *)MALLOC(strlen(array_expr) +
strlen(nexttok) + 1);
/* Roundabout way to do realloc() becase there is no REALLOC() */
strcpy(new_array_expr, array_expr);
strcat(new_array_expr, nexttok);
FREE(array_expr);
array_expr = new_array_expr;
if (strchr(nexttok, ']')) break;
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
}
if (!nexttok) {
Printf("Unterminated net in pin %s\n", array_expr);
}
new_port->net = array_expr;
}
else if (nexttok[0] == '~' || nexttok[0] == '!' || nexttok[0] == '-') {
/* All of these imply that the signal is logically manipulated */
/* in turn implying behavioral code. */
+10 -4
View File
@@ -479,8 +479,12 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
netgen::compare assign "$fnum1 $cell1" "$fnum2 $cell2"
if {[file exists $setupfile]} {
puts stdout "Reading setup file $setupfile"
if {$setupfile == ""} {
puts stdout "\nNo setup file specified. Using trivial default setup.\n"
netgen::permute default ;# transistors and resistors
netgen::property default
} elseif {[file exists $setupfile]} {
puts stdout "\nReading setup file $setupfile\n"
# Instead of sourcing the setup file, run each line so we can
# catch individual errors and not let them halt the LVS process
set perrors 0
@@ -508,8 +512,10 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
puts stdout "Warning: There were errors reading the setup file"
}
} elseif {[string first nosetup $setupfile] < 0} {
netgen::permute default ;# transistors and resistors
netgen::property default
puts stderr "\nError: Setup file $setupfile does not exist.\n"
return
} else {
puts stderr "\nNo setup file specified. Continuing without a setup.\n"
}
if {[string first nolog $logfile] < 0} {
+7 -12
View File
@@ -2902,7 +2902,7 @@ _netcmp_equate(ClientData clientData,
struct ElementClass *saveEclass = NULL;
struct NodeClass *saveNclass = NULL;
int file1, file2;
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0, dounique = 0;
Tcl_Obj *tobj1, *tobj2, *tobj3;
while (objc > 1) {
@@ -2918,6 +2918,11 @@ _netcmp_equate(ClientData clientData,
objv++;
objc--;
}
else if (!strcmp(optstart, "unique")) {
dounique = 1;
objv++;
objc--;
}
else
break;
}
@@ -3342,19 +3347,9 @@ _netcmp_equate(ClientData clientData,
return TCL_ERROR;
}
}
/* Now that all pins are assigned by name, reorder */
/* the pin lists of the 2nd cell to match the */
/* order of the 1st. */
/* Reorder the pin lists of instances of the 2nd */
/* cell to match the order of the 1st. */
// pindata.cell2 = tp2;
// RecurseCellHashTable2(pinorder, (void *)(&pindata));
}
if (EquivalenceClasses(tp1->name, file1, tp2->name, file2)) {
if (EquivalenceClasses(tp1->name, file1, tp2->name, file2, dounique)) {
Fprintf(stdout, "Device classes %s and %s are equivalent.\n",
tp1->name, tp2->name);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));