From c9f7b24e0f56fb83c609ec01f4f1954f0347a351 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 4 Nov 2022 12:07:21 -0400 Subject: [PATCH 1/2] Found an error in property matching causing weird errors in the LVS result. The property matching was failing to match (M=1) to (M!=1) if M was not registered as a property name (which it often isn't). This would allow devices with different numbers of instances in parallel to be put in the same matching group, which then could later identify as a mismatch if the instances were checked in a different order. --- VERSION | 2 +- base/netcmp.c | 47 +++++++++++++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/VERSION b/VERSION index 67f7f06..d27c89c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.240 +1.5.241 diff --git a/base/netcmp.c b/base/netcmp.c index 8d8efbc..5c74c68 100644 --- a/base/netcmp.c +++ b/base/netcmp.c @@ -5911,7 +5911,7 @@ PropertyMatch(struct objlist *ob1, int file1, * single node) has non-zero flags. A non-node entry in the cache * implies a node with zero flags. */ - if (node1->flags != node1->flags) { + if (node1->flags != node2->flags) { Fprintf(stdout, " Parallelized instances disagree on pin connections.\n"); Fprintf(stdout, " Circuit1 instance %s pin %s connections are %s (%d)\n", tp1->instance.name, node1->name, @@ -5932,8 +5932,11 @@ PropertyMatch(struct objlist *ob1, int file1, // PropertySortAndCombine can move the first property, so recompute it // for each circuit. - for (tp1 = ob1; (tp1 != NULL) && tp1->type >= FIRSTPIN; tp1 = tp1->next); - for (tp2 = ob2; (tp2 != NULL) && tp2->type >= FIRSTPIN; tp2 = tp2->next); + tp1 = tp2 = NULL; + if (t1type == PROPERTY) + for (tp1 = ob1; (tp1 != NULL) && tp1->type >= FIRSTPIN; tp1 = tp1->next); + if (t2type == PROPERTY) + for (tp2 = ob2; (tp2 != NULL) && tp2->type >= FIRSTPIN; tp2 = tp2->next); // Find name for printing, removing leading slash if needed. inst1 = ob1->instance.name; @@ -5963,12 +5966,16 @@ PropertyMatch(struct objlist *ob1, int file1, if (vl2->type == PROP_ENDLIST) break; if (vl2 == NULL) continue; if (vl2->key == NULL) continue; - kl2 = (struct property *)HashLookup(vl2->key, &(tc2->propdict)); - if (kl2 != NULL) { - // Allowed for one instance to be missing "M" or "S". - if (!(*matchfunc)(vl2->key, "M") && !(*matchfunc)(vl2->key, "S")) + + // Allowed for one instance to be missing "M" or "S" if the other + // has value 1. + if (!(*matchfunc)(vl2->key, "M") && !(*matchfunc)(vl2->key, "S")) { + kl2 = (struct property *)HashLookup(vl2->key, &(tc2->propdict)); + if (kl2 != NULL) break; // Property is required } + else if (vl2->value.ival != 1) + break; // Property M != 1 or S != 1 is a mismatch. } if (vl2->type != PROP_ENDLIST) { mismatches++; @@ -6016,12 +6023,16 @@ PropertyMatch(struct objlist *ob1, int file1, if (vl1->type == PROP_ENDLIST) break; if (vl1 == NULL) continue; if (vl1->key == NULL) continue; - kl1 = (struct property *)HashLookup(vl1->key, &(tc1->propdict)); - if (kl1 != NULL) { - // Allowed for one instance to be missing "M" or "S". - if (!(*matchfunc)(vl1->key, "M") && !(*matchfunc)(vl1->key, "S")) + + // Allowed for one instance to be missing "M" or "S" if the other + // has value 1. + if (!(*matchfunc)(vl1->key, "M") && !(*matchfunc)(vl1->key, "S")) { + kl1 = (struct property *)HashLookup(vl1->key, &(tc1->propdict)); + if (kl1 != NULL) break; // Property is required } + else if (vl1->value.ival != 1) + break; // Property M != 1 or S != 1 is a mismatch. } if (vl1->type != PROP_ENDLIST) { mismatches++; @@ -6321,7 +6332,7 @@ int ResolveAutomorphsByPin() int portnum; /* Diagnostic */ - Fprintf(stdout, "Resolving automorphisms by pin name.\n"); + Fprintf(stdout, "Resolving symmetries by pin name.\n"); for (NC = NodeClasses; NC != NULL; NC = NC->next) { struct Node *N1, *N2; @@ -6346,6 +6357,8 @@ int ResolveAutomorphsByPin() for (N2 = N1->next; N2 != NULL; N2 = N2->next) { if ((N2->graph != N1->graph) && (*matchfunc)(N2->object->name, N1->object->name)) { + if (Debug == TRUE) + Printf("Symmetry group broken by name match (pin %s)\n", N2->object->name); Magic(newhash); N1->hashval = newhash; N2->hashval = newhash; @@ -6381,7 +6394,7 @@ int ResolveAutomorphsByProperty() unsigned long orighash, newhash; /* Diagnostic */ - Fprintf(stdout, "Resolving automorphisms by property value.\n"); + Fprintf(stdout, "Resolving symmetries by property value.\n"); for (EC = ElementClasses; EC != NULL; EC = EC->next) { struct Element *E1, *E2; @@ -6422,6 +6435,8 @@ int ResolveAutomorphsByProperty() PropertyMatch(E1->object, E1->graph, E2->object, E2->graph, FALSE, FALSE, &result); if (result == 0) { + if (Debug == TRUE) + Printf("Symmetry group split by property (element %s)\n", E2->object->model.class); E2->hashval = newhash; if (E2->graph == E1->graph) C1++; @@ -8115,7 +8130,7 @@ int Compare(char *cell1, char *cell2) /* arbitrarily resolve automorphisms */ Fprintf(stdout, "\n"); - Fprintf(stdout, "Resolving automorphisms by arbitrary symmetry breaking:\n"); + Fprintf(stdout, "Resolving symmetries by arbitrary symmetry breaking:\n"); while ((automorphisms = ResolveAutomorphisms()) > 0) ; if (automorphisms == -1) { MatchFail(cell1, cell2); @@ -8214,7 +8229,7 @@ void NETCOMP(void) else { Printf("Netlists match with %d symmetries.\n", automorphisms); while ((automorphisms = ResolveAutomorphisms()) > 0) - Printf(" automorphisms = %d.\n", automorphisms); + Printf(" symmetries = %d.\n", automorphisms); if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); else if (automorphisms == -2) Fprintf(stdout, "Port counts do not match.\n"); else Printf("Circuits match correctly.\n"); @@ -8281,7 +8296,7 @@ void NETCOMP(void) Printf("(c)reate internal data structure\n"); Printf("do an (i)teration\n"); Printf("(r)un to completion (convergence)\n"); - Printf("(R)un to completion (resolve automorphisms)\n"); + Printf("(R)un to completion (resolve symmetries)\n"); Printf("(v)erify results\n"); Printf("print (a)utomorphisms\n"); Printf("equate two (d)evices\n"); From 7e8508db535064d4e828ae4e4d59fb8f3b4c4ff2 Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Fri, 4 Nov 2022 20:40:37 -0400 Subject: [PATCH 2/2] Additional correction to the property match subroutine to better check instances with permutable pins when checking parallelized instances with disconnected pins vs. mutually connected pins. --- base/netcmp.c | 65 ++++++++++++++++++++-------- base/objlist.h | 19 +++++---- base/spice.c | 12 ++++++ base/verilog.c | 112 +++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 165 insertions(+), 43 deletions(-) diff --git a/base/netcmp.c b/base/netcmp.c index 5c74c68..179cb96 100644 --- a/base/netcmp.c +++ b/base/netcmp.c @@ -5788,14 +5788,15 @@ Tcl_Obj * #else void #endif -PropertyMatch(struct objlist *ob1, int file1, - struct objlist *ob2, int file2, +PropertyMatch(struct Element *E1, struct Element *E2, int do_print, int do_list, int *retval) { struct nlist *tc1, *tc2; - struct objlist *tp1, *tp2, *obn1, *obn2, *tpc; + struct objlist *ob1, *ob2, *tp1, *tp2, *obn1, *obn2, *tpc; struct property *kl1, *kl2; struct valuelist *vl1, *vl2; + struct NodeList *nl1, *nl2; + int file1, file2; int t1type, t2type, run1, run2; int i, mismatches = 0, checked_one; int rval = 1; @@ -5804,6 +5805,12 @@ PropertyMatch(struct objlist *ob1, int file1, Tcl_Obj *proplist = NULL, *mpair, *mlist; #endif + ob1 = E1->object; + ob2 = E2->object; + + file1 = E1->graph; + file2 = E2->graph; + tc1 = LookupCellFile(ob1->model.class, file1); tc2 = LookupCellFile(ob2->model.class, file2); @@ -5891,12 +5898,14 @@ PropertyMatch(struct objlist *ob1, int file1, /* 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. . . */ + /* Permutable pins 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) { + for (tp1 = ob1, tp2 = ob2, nl1 = E1->nodelist; tp1 && tp2; tp1 = tp1->next, tp2 = tp2->next) { struct objlist *node1, *node2; + if ((tp1 != ob1) && (tp1->type <= FIRSTPIN)) break; + if ((tp2 != ob2) && (tp2->type <= FIRSTPIN)) break; + if (file1 == Circuit1->file) node1 = Circuit1->nodename_cache[tp1->node]; else @@ -5907,22 +5916,45 @@ PropertyMatch(struct objlist *ob1, int file1, else node2 = Circuit2->nodename_cache[tp2->node]; + if (node1->flags != node2->flags) { + struct objlist *tp2b; + + /* Find if there is permutable pin node with matching flags */ + /* Note that this is not rigorous, and should be better handled. */ + + for (tp2b = ob2, nl2 = E2->nodelist; tp2b; tp2b = tp2b->next, nl2 = nl2->next) { + if ((tp2b != ob2) && (tp2b->type <= FIRSTPIN)) break; + if (tp2b == tp2) continue; + if (nl2->pin_magic == nl1->pin_magic) { + if (file2 == Circuit1->file) + node2 = Circuit1->nodename_cache[tp2b->node]; + else + node2 = Circuit2->nodename_cache[tp2b->node]; + } + if (node1->flags == node2->flags) break; + } + } + /* NOTE: A "no-connect" node (multiple no-connects represented by a * single node) has non-zero flags. A non-node entry in the cache * implies a node with zero flags. */ if (node1->flags != node2->flags) { - Fprintf(stdout, " Parallelized instances disagree on pin connections.\n"); - Fprintf(stdout, " Circuit1 instance %s pin %s connections are %s (%d)\n", + if (do_print) { + Fprintf(stdout, " Parallelized instances disagree on pin connections.\n"); + Fprintf(stdout, " Circuit1 instance %s pin %s connections are %s (%d)\n", tp1->instance.name, node1->name, (node1->flags == 0) ? "tied together" : "no connects", node1->flags); - Fprintf(stdout, " Circuit2 instance %s pin %s connections are %s (%d)\n", + Fprintf(stdout, " Circuit2 instance %s pin %s connections are %s (%d)\n", tp2->instance.name, node2->name, (node2->flags == 0) ? "tied together" : "no connects", node2->flags); + } mismatches++; } + + nl1 = nl1->next; } // Attempt to organize devices by series and parallel combination @@ -6157,11 +6189,9 @@ PropertyCheck(struct ElementClass *EC, int do_print, int do_list, int *rval) E2 = Etmp; } #ifdef TCL_NETGEN - return PropertyMatch(E1->object, E1->graph, E2->object, E2->graph, - do_print, do_list, rval); + return PropertyMatch(E1, E2, do_print, do_list, rval); #else - PropertyMatch(E1->object, E1->graph, E2->object, E2->graph, - do_print, do_list, rval); + PropertyMatch(E1, E2, do_print, do_list, rval); #endif } @@ -6432,8 +6462,7 @@ int ResolveAutomorphsByProperty() badmatch = FALSE; for (E2 = E1->next; E2 != NULL; E2 = E2->next) { if (E2->hashval != orighash) continue; - PropertyMatch(E1->object, E1->graph, E2->object, E2->graph, - FALSE, FALSE, &result); + PropertyMatch(E1, E2, FALSE, FALSE, &result); if (result == 0) { if (Debug == TRUE) Printf("Symmetry group split by property (element %s)\n", E2->object->model.class); @@ -6503,6 +6532,7 @@ int ResolveAutomorphisms() struct NodeClass *NC; struct Node *N; int C1, C2; + int automorphs; for (EC = ElementClasses; EC != NULL; EC = EC->next) { struct Element *E1, *E2; @@ -6554,8 +6584,9 @@ int ResolveAutomorphisms() FractureElementClass(&ElementClasses); FractureNodeClass(&NodeClasses); ExhaustiveSubdivision = 1; - while (!Iterate() && VerifyMatching() >= 0); - return(VerifyMatching()); + while (!Iterate() && (VerifyMatching() >= 0)); + + return VerifyMatching(); } /*------------------------------------------------------*/ diff --git a/base/objlist.h b/base/objlist.h index 6845a29..6facb4a 100644 --- a/base/objlist.h +++ b/base/objlist.h @@ -206,7 +206,7 @@ struct nlist { char *name; int number; /* number of instances defined */ int dumped; /* instance count, and general-purpose marker */ - unsigned char flags; + unsigned short flags; unsigned char class; unsigned long classhash; /* randomized hash value for cell class */ struct Permutation *permutes; /* list of permuting pins */ @@ -222,17 +222,18 @@ struct nlist { /* Defined nlist structure flags */ -#define CELL_MATCHED 0x01 /* cell matched to another */ -#define CELL_NOCASE 0x02 /* cell is case-insensitive (e.g., SPICE) */ -#define CELL_TOP 0x04 /* cell is a top-level cell */ -#define CELL_PLACEHOLDER 0x08 /* cell is a placeholder cell */ -#define CELL_PROPSMATCHED 0x10 /* properties matched to matching cell */ -#define CELL_DUPLICATE 0x20 /* cell has a duplicate */ +#define CELL_MATCHED 0x001 /* cell matched to another */ +#define CELL_NOCASE 0x002 /* cell is case-insensitive (e.g., SPICE) */ +#define CELL_TOP 0x004 /* cell is a top-level cell */ +#define CELL_PLACEHOLDER 0x008 /* cell is a placeholder cell */ +#define CELL_PROPSMATCHED 0x010 /* properties matched to matching cell */ +#define CELL_DUPLICATE 0x020 /* cell has a duplicate */ +#define CELL_VERILOG 0x040 /* cell is verilog module */ /* Flags for combination allowances and prohibitions */ -#define COMB_SERIES 0x40 -#define COMB_NO_PARALLEL 0x80 +#define COMB_SERIES 0x100 +#define COMB_NO_PARALLEL 0x200 extern struct nlist *CurrentCell; extern struct objlist *CurrentTail; diff --git a/base/spice.c b/base/spice.c index 55b9a5f..e888bd5 100644 --- a/base/spice.c +++ b/base/spice.c @@ -1783,6 +1783,18 @@ skip_ends: ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */ update = 1; } + else if (tp->flags & CELL_VERILOG) { + if (tp->flags & CELL_PLACEHOLDER) { + /* Flag this as an error. To do: Rearrange the verilog instance pins to */ + /* match the SPICE subcircuit pin order. */ + Fprintf(stderr, "Error: SPICE subcircuit %s should be read before verilog " + "module using it, or pins may not match!\n", subcktname); + } + else { + Fprintf(stderr, "Error: SPICE subcircuit %s redefines a verilog module!\n", + subcktname); + } + } /* nexttok is now NULL, scan->name points to class */ diff --git a/base/verilog.c b/base/verilog.c index b3cba76..4598b1c 100644 --- a/base/verilog.c +++ b/base/verilog.c @@ -803,6 +803,56 @@ extern void IncludeVerilog(char *, int, struct cellstack **, int); extern void PushStack(char *cellname, struct cellstack **top); extern void PopStack(struct cellstack **top); +/*------------------------------------------------------*/ +/* Callback routine for FindInstanceOf() */ +/* NOTE: This casts a (struct objlist) pointer to a */ +/* (struct nlist) pointer for the purpose of using */ +/* RecurseCellHashTable2(). FindInstanceOf() casts it */ +/* back into a (struct objlist) pointer. */ +/*------------------------------------------------------*/ + +struct nlist *findInstance(struct hashlist *p, void *clientdata) +{ + struct nlist *ptr; + struct objlist *ob; + struct nlist *tref = (struct nlist *)clientdata; + + ptr = (struct nlist *)(p->ptr); + if (ptr->file != tref->file) return NULL; + + ob = LookupInstance(tref->name, ptr); + return (struct nlist *)ob; +} + +/*------------------------------------------------------*/ +/* Routine to find the first instance of a cell */ +/*------------------------------------------------------*/ + +struct objlist *FindInstanceOf(struct nlist *tc) +{ + return (struct objlist *)RecurseCellHashTable2(findInstance, (void *)tc); +} + +/*------------------------------------------------------*/ +/* Given a reference cell pointer tref and a port name */ +/* portname, check if portname is a port of tref. If */ +/* not, then call Port() to add one. If tref is NULL, */ +/* then always add the port. */ +/*------------------------------------------------------*/ + +void CheckPort(struct objlist *tref, char *portname) +{ + struct objlist *ob; + + if (tref != NULL) { + for (ob = CurrentCell->cell; ob && (ob->type == PORT); ob = ob->next) { + if ((*matchfunc)(ob->name, portname)) + return; + } + } + Port(portname); +} + /*------------------------------------------------------*/ /* Read a verilog structural netlist */ /*------------------------------------------------------*/ @@ -818,7 +868,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr, struct keyvalue *kvlist = NULL; char inst[MAX_STR_LEN], model[MAX_STR_LEN], instname[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN]; struct nlist *tp; - struct objlist *parent, *sobj, *nobj, *lobj, *pobj; + struct objlist *parent, *sobj, *nobj, *lobj, *pobj, *cref; inst[MAX_STR_LEN-1] = '\0'; model[MAX_STR_LEN-1] = '\0'; @@ -938,6 +988,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr, InputParseError(stderr); } in_module = (char)1; + cref = NULL; /* Save pointer to current cell */ if (CurrentCell != NULL) @@ -949,6 +1000,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr, snprintf(model, MAX_STR_LEN-1, "%s", nexttok); tp = LookupCellFile(nexttok, filenum); + hasports = (char)0; /* Check for name conflict with duplicate cell names */ /* This may mean that the cell was used before it was */ @@ -986,23 +1038,48 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr, CellDef(nexttok, filenum); tp = LookupCellFile(nexttok, filenum); } - else if (tp != NULL) { /* Make a new definition for an empty cell */ - FreePorts(nexttok); - CellDelete(nexttok, filenum); /* This removes any PLACEHOLDER flag */ + else if (tp != NULL) { /* Cell exists, but as a placeholder */ + struct nlist *tptmp = NULL; + char ctemp[8]; + int n = 0; + + /* This redefines a placeholder module to an unused temporary cell name */ + while (1) { + sprintf(ctemp, "%d", n); + tptmp = LookupCellFile(ctemp, filenum); + if (tptmp == NULL) break; + n++; + } + CellRehash(nexttok, ctemp, filenum); + tptmp = LookupCellFile(ctemp, filenum); + + /* Create a new module definition */ CellDef(model, filenum); tp = LookupCellFile(model, filenum); + + /* Find an instance of this module in the netlist */ + cref = FindInstanceOf(tp); + if ((cref != NULL) && (cref->name != NULL)) { + hasports = (char)1; + /* Copy ports from the original parent cell to the new parent cell */ + for (pobj = tptmp->cell; pobj && (pobj->type == PORT); pobj = pobj->next) + Port(pobj->name); + } + /* Remove the original cell definition */ + FreePorts(ctemp); + CellDelete(ctemp, filenum); /* This removes any PLACEHOLDER flag */ } else if (tp == NULL) { /* Completely new cell, no name conflict */ CellDef(model, filenum); tp = LookupCellFile(model, filenum); } - hasports = (char)0; inlined_decls = (char)0; if (tp != NULL) { struct bus wb, *nb; + tp->flags |= CELL_VERILOG; PushStack(tp->name, CellStackPtr); /* Need to support both types of I/O lists: Those */ @@ -1071,7 +1148,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr, if (GetBusTok(&wb) != 0) { // Didn't parse as a bus, so wing it wb.start = wb.end = -1; - Port(nexttok); + CheckPort(cref, nexttok); } } else { @@ -1079,13 +1156,13 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr, if (wb.start > wb.end) { for (i = wb.start; i >= wb.end; i--) { sprintf(portname, "%s[%d]", nexttok, i); - Port(portname); + CheckPort(cref, portname); } } else { for (i = wb.start; i <= wb.end; i++) { sprintf(portname, "%s[%d]", nexttok, i); - Port(portname); + CheckPort(cref, portname); } } /* Also register this port as a bus */ @@ -1097,7 +1174,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr, wb.start = wb.end = -1; } else { - Port(nexttok); + CheckPort(cref, nexttok); } } hasports = 1; @@ -1153,7 +1230,7 @@ skip_endmodule: if (GetBusTok(&wb) != 0) { // Didn't parse as a bus, so wing it wb.start = wb.end = -1; - Port(nexttok); + CheckPort(cref, nexttok); } } else if (!match(nexttok, ",")) { @@ -1161,13 +1238,13 @@ skip_endmodule: if (wb.start > wb.end) { for (i = wb.start; i >= wb.end; i--) { sprintf(portname, "%s[%d]", nexttok, i); - Port(portname); + CheckPort(cref, portname); } } else { for (i = wb.start; i <= wb.end; i++) { sprintf(portname, "%s[%d]", nexttok, i); - Port(portname); + CheckPort(cref, portname); } } /* Also register this port as a bus */ @@ -1178,7 +1255,7 @@ skip_endmodule: wb.start = wb.end = -1; } else { - Port(nexttok); + CheckPort(cref, nexttok); } } hasports = 1; @@ -1194,6 +1271,7 @@ skip_endmodule: InputParseError(stderr); } in_module = (char)0; + cref = NULL; if (*CellStackPtr) PopStack(CellStackPtr); if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum); @@ -2115,7 +2193,7 @@ nextinst: sprintf(localnet, "_noconnect_%d_", localcount++); Node(localnet); join(localnet, obptr->name); - Fprintf(stderr, + Fprintf(stdout, "Note: Implicit pin %s in instance %s of %s in cell %s\n", obpinname, locinst, modulename, CurrentCell->name); } @@ -2207,8 +2285,8 @@ nextinst: char tempname[MAX_STR_LEN]; int maxnode; - /* This pin was probably implicit in the first call */ - /* and so it needs to be added to the definition. */ + /* This pin was probably implicit in the first call */ + /* and so it needs to be added to the definition. */ ReopenCellDef(modulename, filenum); Port(scan->name); @@ -2265,7 +2343,7 @@ nextinst: sprintf(tempname, "_noconnect_%d_", localcount++); Node(tempname); join(tempname, nobj->name); - Fprintf(stderr, "Note: Implicit pin %s in instance " + Fprintf(stdout, "Note: Implicit pin %s in instance " "%s of %s in cell %s\n", scan->name, sobj->instance.name, modulename, CurrentCell->name);