Compare commits

...
69 Commits
Author SHA1 Message Date
Tim Edwards 550234dce5 Merge branch 'master' into netgen-1.5 2021-06-15 03:00:36 -04:00
Tim Edwards 92dfa74403 Corrected an issue that was caused by introducing a type of parallel
device with one or more no-connect pins.  The flag that indicates a
no-connect pin was checked incorrectly, potentially causing obscure
and misleading property mismatch messages to be generated.
2021-06-14 14:30:10 -04:00
Tim Edwards a4c0028706 Merge branch 'master' into netgen-1.5 2021-06-13 03:00:32 -04:00
Tim Edwards d53541d1d3 Correction to previous commit (failed to link to last pointer
after processing properties during flattening).
2021-06-12 20:15:55 -04:00
Tim Edwards 57b2d21221 Modified the way flattening is done to account for multiple property
records, which were being ignored.  This really only applies to
parallel subcircuits being flattened.  To flatten correctly requires
that any circuit with N property records must be flattened into the
parent at least N times.  To do:  Must look for M > 1 records in the
properties and flatten (M - 1) additional times.
2021-06-12 17:37:50 -04:00
Tim Edwards e7ae84bbc8 Merge branch 'master' into netgen-1.5 2021-06-10 03:00:09 -04:00
Tim Edwards dfabdaa367 Modified the JSON output so that missing pins which do not match
unconnected pins in the other netlist are marked as "(no pin)"
rather than "(no matching pin)".  This allows a script parsint the
JSON file (e.g., count_lvs.py from qflow) to distinguish between a
real pin error and one that can be ignored.
2021-06-09 09:34:02 -04:00
Tim Edwards c5c1306f6a Corrected a minor error where the property mismatch check outputs
an error message while doing a non-final iteration, where all
output is supposed to be suppressed.
2021-06-09 09:20:45 -04:00
Tim Edwards 43c018f3bc Merge branch 'master' into netgen-1.5 2021-06-09 03:00:10 -04:00
Tim Edwards 0614461c84 Modified MatchPins so that pin mismatches on nets that are not
connected to anything are once again ignored (the prior commit
to prevent netgen from not reporting swapped pins as an error
overcorrected).
2021-06-08 17:25:17 -04:00
Tim Edwards 6e94886b65 Messed up basic pin comparison between black-box vs. non-black-box
subcircuit situations in the last commit.  This commit fixes it.
2021-06-08 16:52:57 -04:00
Tim Edwards ca119edc0a Merge branch 'master' into netgen-1.5 2021-06-04 03:00:10 -04:00
Tim Edwards 0556abf3c1 Finally corrected the issue with pins being able to be swapped
without netgen noticing---this behavior got broken by an
exception for "black box" circuits, but failed to check if the
circuit really was marked as a "black box" or not.  Fixing that
revealed another issue with verilog implicit pins.  Both issues
have now been corrected.
2021-06-03 17:42:54 -04:00
Tim Edwards 84348d2fe1 Merge branch 'master' into netgen-1.5 2021-05-31 03:00:10 -04:00
Tim Edwards 3356dd784e Made a number of usability corrections to the python GUI for
netgen.  More work will be done later.  Also:  Removed the
derived file lvs_manager.py from the repository, and modified
the Makefile to remove it as part of "make clean".
2021-05-30 20:39:47 -04:00
Tim Edwards 813b29cdc4 Merge branch 'master' into netgen-1.5 2021-05-29 03:00:10 -04:00
Tim Edwards 00949970b4 The previous method change for sorting to accommodate capacitors and
other devices that do not have a "critical property" that determines
which properties can add together was flawed and messed up the sorting
of devices like transistors and resistors that do have a critical
property.  Reworked the sorting order so that it makes sense for both
situations.
2021-05-28 21:20:39 -04:00
Tim Edwards ccf4a0a900 Merge branch 'master' into netgen-1.5 2021-05-27 03:00:32 -04:00
Tim Edwards c83b6def5b Corrected an issue with swapped arguments to PromoteProperty().
Also corrected a superficial issue with an attempt to print out an
instance name that doesn't exist.  There is an underlying bug here
related to cells that have no pins and/or no contents which has not
been debugged.  The fix just keeps netgen from segfaulting.
2021-05-26 12:46:57 -04:00
Tim Edwards e34166fdb9 Merge branch 'master' into netgen-1.5 2021-05-26 03:00:32 -04:00
Tim Edwards 8fc2c097d2 Added one modification that automatically ignores any cell that has
no pins at all.  This overrides the default behavior of treating
empty cells as "black-box" entries, and avoids attempts to compare
cells like logo artwork.
2021-05-25 12:41:57 -04:00
Tim Edwards a13491e358 Merge branch 'master' into netgen-1.5 2021-05-25 03:00:09 -04:00
Tim Edwards 2ac2a3b6f8 Fixed a bad assumption; PropertyOptimize allows devices with
different additive properties (like transistor width) to combine;
this is totally wrong and may have been left over from code written
before the routine was split into simple combinations (add similar
devices) and agressive combinations (e.g., add widths together).
2021-05-24 10:05:10 -04:00
Tim Edwards 1d05b8b676 Merge branch 'master' into netgen-1.5 2021-05-20 03:00:31 -04:00
Tim Edwards 759d63cea9 Changed a comment in the code that no longer applies after the last
modification.
2021-05-19 16:42:03 -04:00
Tim Edwards 476da015f0 Added support for expression parameters to be added to a device
class during setup, for the purpose of generating some derived
value that is used for merging and sorting, such as area = l*w.
Note that this likely needs adjusting so that the expression is
evaluated but not replaced for the purpose of sorting, since the
values to the parameter may change after parallel and series
merging.
2021-05-19 16:27:52 -04:00
Tim Edwards 4aa8a72769 Corrected an error found in ResolveAutomorphsByProperty which would
cause inexplicable output in case of a property error by showing a
netlist topography error instead of a property error (but the output
shows that the netlists match, and there is no reporting of any
property errors).  This error was discovered while implementing a
better sorting method for parallel combination.  The improved method
sorts on two properties rather than one, and so should not fall into
the error where, say, devices are sorted on W but have different L
for a device like a capacitor where no "critical" property is
specified (and other similar cases, although that is a common one).
2021-05-19 14:56:38 -04:00
Tim Edwards e97d6f1aeb Merge branch 'master' into netgen-1.5 2021-05-14 03:00:10 -04:00
Tim Edwards 47e7367c26 Updated the versio with the last commit. 2021-05-13 15:58:49 -04:00
Tim Edwards 362fb7120b Modified the behavior of the parallel combination of devices with
"similar no connect" pins so that it is not done on top-level
netlists.  This is mainly to deal with the problem where connections
that would normally be pins (but may have, for example, been
connected to a voltage source in a schematic that was deleted because
it was not a netlistable device) are treated as no-connects.
The parallel combination of devices with similar no-connects can then
differ between netlists that differ in describing transistors as
fingered vs. individual devices.  This is an obscure case, but the
output of netgen then becomes not only wrong but difficult to
understand what happened, so it is better to avoid.
2021-05-13 15:50:12 -04:00
Tim Edwards fb4759a8cd Merge branch 'master' into netgen-1.5 2021-05-04 03:00:10 -04:00
Tim Edwards 92e8fed8be Corrected an error in checking merge flags when doing sorting of
parallel and series devices.  This was a fairly major error
undermining the property sorting (the case where the same number
of devices are in parallel in both circuits and need to be sorted
by, e.g., width, prior to checking for matches).
2021-05-03 11:20:44 -04:00
Tim Edwards 45d4004d43 Merge branch 'master' into netgen-1.5 2021-05-02 03:00:09 -04:00
Tim Edwards 01b2484be3 Updated version. 2021-05-01 22:49:59 -04:00
Tim Edwards fe47d84ace Missed a corner case in the series combination code where a device
has been shuffled to the beginning of the cell, which requires
different handling.
2021-05-01 22:48:59 -04:00
Tim Edwards 0b014e6efd Merge branch 'master' into netgen-1.5 2021-03-20 03:00:09 -04:00
Tim Edwards 0a2adeb593 Modified the "flatten" command to allow class "module" to be
flattened.  This is marginally useful---since the "module" class
is a black box, it is essentially the same as using the "ignore"
command.
2021-03-19 13:57:11 -04:00
Tim Edwards 330b34139c Slight modification to print the list of cells being forced to
match for both the filename option and immediate list.
2021-03-19 10:51:42 -04:00
Tim Edwards 62d0352149 Modified the "-noflatten" command option to "lvs" so that it can be
given a filename as a value in addition to being passed a list of
cells directly.
2021-03-19 09:41:42 -04:00
Tim Edwards 9ca713dfd9 Merge branch 'master' into netgen-1.5 2021-03-18 03:00:29 -04:00
Tim Edwards b1b04b8e87 Corrected a statement related to proxy pins that can cause a
segfault condition.
2021-03-17 16:31:22 -04:00
Tim Edwards 851a1f941e Merge branch 'master' into netgen-1.5 2021-03-06 03:00:09 -05:00
Tim Edwards 6b9d92de65 Corrected some pointer-to-int and int-to-pointer conversions to
keep the compiler happy.
2021-03-05 20:19:04 -05:00
Tim Edwards d763ad0e60 First and simplest (but most effective) cut at aligning the two
sides of the output when presenting side-by-side results.  This
matches up net or device names within a partition.  Where net or
device names match, the contents are also aligned on either side.
I attempted to also do a "best match" of contents between sides,
but as this involves a lot of analyzing the contents, it is very
computationally expensive, and so the code has been disabled.  It
could be added back in as an option.  There are also various ways
to optimize it for speed.
2021-03-05 17:23:22 -05:00
Tim Edwards fd72e24a86 Corrected an error in the code that handles no-connects as valid
pins for enabling parallel combinations, which could cause a crash.
Added a "-force" option to "equate pins" to allow pins to be matched
even on subcircuits that did not correctly match;  this was done in
conjuction with an extra option to the "lvs" command "-noflatten="
to pass a list of cellname to not be flattened even if they do not
match.  This is generally discouraged, as it prevents netgen from
resolving differences between layout and schematic hierarchy, but it
can be useful for checking that the hierarchy above a certain cell
is correct, given that if a subcell is really unmatched, then its
errors will keep propagating up the top level, making additional
errors hard to diagnose.
2021-03-05 11:32:17 -05:00
Tim Edwards 20077d3d56 Modifed the handling of parallelized no-connects so that the behavior
can be turned on or off from the setup using "property parallel open"
to allow parallelizing devices with no-connect pins vs. "property
parallel connected" to only allow parallelizing of devices with all
pins connected.
2021-03-03 17:31:45 -05:00
Tim Edwards fb798d6ce7 Merge branch 'master' into netgen-1.5 2021-03-03 03:00:12 -05:00
Tim Edwards a21ba820e9 Corrected a missing variable in a print statement in the SPICE read
routine that can cause a segfault.
2021-03-02 21:48:48 -05:00
Tim Edwards 792c5e569a Resolved the case mentioned in a prior commit where the case of N
devices in parallel with unconnected pins would be confused with
N devices in parallel with those pins all tied together.  This is
treated as a property error.
2021-03-02 16:49:03 -05:00
Tim Edwards f92192efd4 Corrected an error from yesterday's commit; the test case for
parallel combinations with disconnected nodes works with the error,
but in general it won't.
2021-03-02 10:58:36 -05:00
Tim Edwards 1f50c7ccc5 Updating VERSION with pull request #18 on github. 2021-03-02 10:16:54 -05:00
Ahmed Ghazy 8282460774 Remove netgen.{sh,tcl} and add them to .gitignore 2021-03-02 10:24:17 +02:00
Tim Edwards a888502038 Merge branch 'master' into netgen-1.5 2021-03-02 03:00:35 -05:00
Tim Edwards ea07642172 Removed the symmetry breaking option from the scripted "lvs"
command.
2021-03-01 16:57:49 -05:00
Tim Edwards e9da037001 Removed the code for fast symmetry breaking, as it has been found
to generate incorrect results on occasion.  The method to parallelize
cells with the same no-connect pins should avoid the worst-case
symmetry breaking that was previously plaguing the LVS of large
standard-cell layouts.
2021-03-01 16:55:07 -05:00
Tim Edwards 7ee50a3f8f Modified the parallel combination code to treat cells as equivalently
parallel if the same pins are no-connects.  These were previously not
treated as parallel because each no-connect has a unique node number,
and cells were only considered as parallel if all pins connected to
the same node numbers.  This avoids issues with long-running symmetry
breaking on standard cell designs due to cells like antenna taps or
any cell that is placed without connecting it up.  To do:  This
makes indistinguishable certain cases, e.g., N cells in circuit 1
with pin X open vs. N cells in circuit 2 with pin X all tied together.
This could be caught during property matching.
2021-03-01 16:33:55 -05:00
Tim Edwards c16258d4d1 Merge branch 'master' into netgen-1.5 2021-02-25 03:00:36 -05:00
Tim Edwards c7848c9c02 Corrected an error that (in a rare circumstance) can cause netgen
to go into an infinite loop and fill memory until it crashes, due
to a complete pin mismatch between devices causing one device to
have its pins removed and replaced with proxy pins.
2021-02-24 16:12:19 -05:00
Tim Edwards 5341d7a5fd Merge branch 'master' into netgen-1.5 2021-02-17 03:00:31 -05:00
Tim Edwards 402e1f0f25 Found a chokepoint in FlattenInstancesOf that was unnecessary as it
was running through the entire object linked list to find the
predecessor of a record that it had already found.  Solved by simply
keeping track of the predecessor record.
2021-02-16 17:12:00 -05:00
Tim Edwards 8395df633c Merge branch 'master' into netgen-1.5 2021-02-10 03:00:16 -05:00
Tim Edwards 19471275df Added handling of control blocks (.CONTROL ... .ENDC) in ngspice-
format files to the SPICE parser, so that netgen can be run
directly on a testbench file and not generate errors due to
statements in the control block.
2021-02-09 09:30:27 -05:00
Tim Edwards e75f5661e2 Added a zero-value current source to the zero-valued devices handled
by the pre-matching method.  The current source is treated like the
others except that it forms an open circuit rather than a short.
2021-02-09 09:12:22 -05:00
Tim Edwards 6554063df3 Merge branch 'master' into netgen-1.5 2021-01-22 03:00:40 -05:00
Tim Edwards a034fc16d6 Updated VERSION. 2021-01-21 13:41:20 -05:00
Tim Edwards 62fbd422e2 Modified netcmp.c "addproxies" routine to not get confused by cells
with no pins marked with a "(no pins)" placeholder.  Otherwise it
goes into an infinite loop and eats up memory until it crashes.
2021-01-21 13:38:44 -05:00
Tim Edwards 4416a380b3 Merge branch 'master' into netgen-1.5 2021-01-18 03:00:51 -05:00
Tim Edwards d0bae6aff4 Updated VERSION with the merge of pull request #15 from Alessandro de
Laurenzis.
2021-01-17 11:38:11 -05:00
Alessandro De Laurenzis 8a7bb7f2ca Fix missing prototype for ReadVerilogFile function
Unbreak OpenBSD port for mips64 arch
2021-01-17 10:21:52 +01:00
16 changed files with 1920 additions and 2032 deletions
+2
View File
@@ -12,3 +12,5 @@ scripts/config.status
*~ *~
*.log *.log
UPDATE_ME UPDATE_ME
tcltk/netgen.sh
tcltk/netgen.tcl
+1 -1
View File
@@ -1 +1 @@
1.5.163 1.5.187
+316 -208
View File
@@ -193,17 +193,19 @@ void flattenCell(char *name, int file)
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr); if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
FreeString(tmp->name); FreeString(tmp->name);
tmp->name = strsave(tmpstr); tmp->name = strsave(tmpstr);
#if OLDPREFIX
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->instance.name);
#else
strcpy(tmpstr+prefixlength,tmp->instance.name);
#endif
FreeString(tmp->instance.name);
tmp->instance.name = strsave(tmpstr);
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict)); HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
if (tmp->type == FIRSTPIN) if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict)); #if OLDPREFIX
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->instance.name);
#else
strcpy(tmpstr+prefixlength,tmp->instance.name);
#endif
FreeString(tmp->instance.name);
tmp->instance.name = strsave(tmpstr);
if (tmp->type == FIRSTPIN)
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
}
} }
/* splice instance out of parent */ /* splice instance out of parent */
@@ -252,9 +254,10 @@ void flattenCell(char *name, int file)
int flattenInstancesOf(char *name, int fnum, char *instance) int flattenInstancesOf(char *name, int fnum, char *instance)
{ {
struct objlist *ParentParams; struct objlist *ParentParams;
struct objlist *ParentProps; struct objlist *ParentProps, *CurrentProp;
struct objlist *NextObj; struct objlist *NextObj, *LastObj, *prepp;
struct objlist *ChildObjList; struct objlist *ChildObjList, *ChildListEnd;
struct objlist *ChildStart, *ChildEnd, *ParentEnd;
struct nlist *ThisCell; struct nlist *ThisCell;
struct nlist *ChildCell; struct nlist *ChildCell;
struct objlist *tmp, *ob2, *ob3; struct objlist *tmp, *ob2, *ob3;
@@ -296,21 +299,35 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
while (notdone) { while (notdone) {
notdone = 0; notdone = 0;
ParentParams = ThisCell->cell; ParentParams = ThisCell->cell;
LastObj = NULL;
for (ParentParams = ThisCell->cell; ParentParams != NULL; for (ParentParams = ThisCell->cell; ParentParams != NULL;
ParentParams = NextObj) { ParentParams = NextObj) {
if (Debug) Printf("Parent = %s, type = %d\n", if (Debug) Printf("Parent = %s, type = %d\n",
ParentParams->name, ParentParams->type); ParentParams->name, ParentParams->type);
NextObj = ParentParams->next; NextObj = ParentParams->next;
if (ParentParams->type != FIRSTPIN) continue; if (ParentParams->type != FIRSTPIN) {
if (!(*matchfunc)(ParentParams->model.class, instance)) continue; LastObj = ParentParams;
continue;
}
if (!(*matchfunc)(ParentParams->model.class, instance)) {
LastObj = ParentParams;
continue;
}
ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file); ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file);
if (Debug) if (Debug)
Printf(" Flattening instance: %s, primitive = %s\n", Printf(" Flattening instance: %s, primitive = %s\n",
ParentParams->instance.name, (ChildCell->class == ParentParams->instance.name, (ChildCell->class ==
CLASS_SUBCKT) ? "no" : "yes"); CLASS_SUBCKT) ? "no" : "yes");
if (ChildCell->class != CLASS_SUBCKT) continue; if ((ChildCell->class != CLASS_SUBCKT) && (ChildCell->class != CLASS_MODULE)) {
if (ChildCell == ThisCell) continue; // Avoid infinite loop LastObj = ParentParams;
continue;
}
if (ChildCell == ThisCell) {
LastObj = ParentParams;
continue; // Avoid infinite loop
}
/* Does the parent cell have properties? If so, save a pointer to them */ /* Does the parent cell have properties? If so, save a pointer to them */
for (ParentProps = ParentParams->next; ParentProps && for (ParentProps = ParentParams->next; ParentProps &&
@@ -318,202 +335,250 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
ParentProps = ParentProps->next) { ParentProps = ParentProps->next) {
if (ParentProps->type == PROPERTY) break; if (ParentProps->type == PROPERTY) break;
} }
if (ParentProps && (ParentProps->type != PROPERTY)) ParentProps = NULL; if (ParentProps && (ParentProps->type != PROPERTY)) {
ParentProps = NULL;
/* not primitive, so need to flatten this instance */ CurrentProp = NULL;
notdone = 1;
/* if this is a new instance, flatten it */
/* if (ChildCell->dumped == 0) flattenCell(ParentParams->model.class, file); */
ChildObjList = CopyObjList(ChildCell->cell, 1);
numflat++;
/* update node numbers in child to unique numbers */
oldmax = 0;
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
if (tmp->node > oldmax) oldmax = tmp->node;
if (nextnode <= oldmax) nextnode = oldmax + 1;
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
if (tmp->node <= oldmax && tmp->node > 0) {
if (Debug) Printf("Update node %d --> %d\n", tmp->node, nextnode);
UpdateNodeNumbers(ChildObjList, tmp->node, nextnode);
nextnode++;
}
/* copy nodenumbers of ports from parent */
ob2 = ParentParams;
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next)
if (IsPort(tmp)) {
if (tmp->node > 0) {
if (ob2->node == -1) {
// Before commiting to attaching to a unconnected node, see
// if there is another node in ParentParams with the same
// name and a valid node number. If so, connect them. In
// the broader case, it may be necessary to consider all
// nodes, not just those with node == -1, and call join()
// here to update all node numbers in the parent cell.
// In that case, a more efficient method is needed for
// tracking same-name ports.
for (ob3 = ParentParams; ob3 && ob3->type >= FIRSTPIN; ob3 = ob3->next) {
if (ob3 == ob2) continue;
if ((*matchfunc)(ob3->name, ob2->name) && ob3->node != -1) {
ob2->node = ob3->node;
break;
}
}
}
if (Debug) {
// Printf(" Sealing port: %d to node %d\n", tmp->node, ob2->node);
Printf("Update node %d --> %d\n", tmp->node, ob2->node);
}
UpdateNodeNumbers(ChildObjList, tmp->node, ob2->node);
}
/* in pathological cases, the lengths of the port lists may
change. This is an error, but that is no reason to allow
the code to core dump. We avoid this by placing a
superfluous check on ob2->type
*/
if (ob2 != NULL)
ob2 = ob2->next;
if (ob2 == NULL) break;
}
/* Using name == NULL to indicate that a .ext file is being */
/* flattened on the fly. This is quick & dirty. */
if (name != NULL) {
/* delete all port elements from child */
while ((ChildObjList != NULL) && IsPort(ChildObjList)) {
/* delete all ports at beginning of list */
if (Debug) Printf("deleting leading port from child\n");
tmp = ChildObjList->next;
// FreeObjectAndHash(ChildObjList, ChildCell);
FreeObject(ChildObjList);
ChildObjList = tmp;
}
tmp = ChildObjList;
while (tmp && (tmp->next != NULL)) {
if (IsPort(tmp->next)) {
ob2 = (tmp->next)->next;
if (Debug) Printf("deleting a port from child\n");
// FreeObjectAndHash(tmp->next, ChildCell);
FreeObject(tmp->next);
tmp->next = ob2;
}
else tmp = tmp->next;
}
} }
else
CurrentProp = ParentProps;
/* for each element in child, prepend 'prefix' */ /* Find the end record of the parent cell and save it */
for (ParentEnd = (ParentProps) ? ParentProps : ParentParams;
ParentEnd && ParentEnd->next && ParentEnd->next->type != FIRSTPIN;
ParentEnd = ParentEnd->next);
/* Not primitive, so need to flatten this instance */
notdone = 1;
/* Loop over property records. Need to flatten once per */
/* property record (or more, if property has M > 1) */
ob2 = NULL;
ChildObjList = NULL;
ChildListEnd = NULL;
while (1) {
ChildStart = CopyObjList(ChildCell->cell, 1);
numflat++;
/* Find the end record of the child cell and save it */
for (ChildEnd = ChildStart; ChildEnd && ChildEnd->next;
ChildEnd = ChildEnd->next);
if (ChildListEnd == NULL) ChildListEnd = ChildEnd;
/* update node numbers in child to unique numbers */
oldmax = 0;
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next)
if (tmp->node > oldmax) oldmax = tmp->node;
if (nextnode <= oldmax) nextnode = oldmax + 1;
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next)
if (tmp->node <= oldmax && tmp->node > 0) {
if (Debug) Printf("Update node %d --> %d\n", tmp->node, nextnode);
UpdateNodeNumbers(ChildStart, tmp->node, nextnode);
nextnode++;
}
/* copy nodenumbers of ports from parent */
ob2 = ParentParams;
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) {
if (IsPort(tmp)) {
if (tmp->node > 0) {
if (ob2->node == -1) {
// Before commiting to attaching to a unconnected node, see
// if there is another node in ParentParams with the same
// name and a valid node number. If so, connect them. In
// the broader case, it may be necessary to consider all
// nodes, not just those with node == -1, and call join()
// here to update all node numbers in the parent cell.
// In that case, a more efficient method is needed for
// tracking same-name ports.
for (ob3 = ParentParams; ob3 && ob3->type >= FIRSTPIN;
ob3 = ob3->next) {
if (ob3 == ob2) continue;
if ((*matchfunc)(ob3->name, ob2->name) && ob3->node != -1) {
ob2->node = ob3->node;
break;
}
}
}
if (Debug) {
// Printf(" Sealing port: %d to node %d\n", tmp->node, ob2->node);
Printf("Update node %d --> %d\n", tmp->node, ob2->node);
}
UpdateNodeNumbers(ChildStart, tmp->node, ob2->node);
}
/* in pathological cases, the lengths of the port lists may
* change. This is an error, but that is no reason to allow
* the code to core dump. We avoid this by placing a
* superfluous check on ob2->type
*/
if (ob2 != NULL) ob2 = ob2->next;
if (ob2 == NULL) break;
}
}
/* Using name == NULL to indicate that a .ext file is being */
/* flattened on the fly. This is quick & dirty. */
if (name != NULL) {
/* delete all port elements from child */
while ((ChildStart != NULL) && IsPort(ChildStart)) {
/* delete all ports at beginning of list */
if (Debug) Printf("deleting leading port from child\n");
tmp = ChildStart->next;
FreeObject(ChildStart);
ChildStart = tmp;
}
tmp = ChildStart;
while (tmp && (tmp->next != NULL)) {
if (IsPort(tmp->next)) {
ob2 = (tmp->next)->next;
if (Debug) Printf("deleting a port from child\n");
FreeObject(tmp->next);
tmp->next = ob2;
}
else tmp = tmp->next;
}
}
/* for each element in child, prepend 'prefix' */
#if !OLDPREFIX #if !OLDPREFIX
/* replaces all the sprintf's below */ /* replaces all the sprintf's below */
strcpy(tmpstr,ParentParams->instance.name); strcpy(tmpstr,ParentParams->instance.name);
strcat(tmpstr,SEPARATOR); strcat(tmpstr,SEPARATOR);
prefixlength = strlen(tmpstr); prefixlength = strlen(tmpstr);
#endif #endif
for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) { for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) {
if (tmp->type == PROPERTY) if (tmp->type == PROPERTY)
continue; continue;
else if (IsGlobal(tmp)) { else if (IsGlobal(tmp)) {
/* Keep the name but search for node of same name in parent */ /* Keep the name but search for node of same name in parent */
/* and replace the node number, if found. */ /* and replace the node number, if found. */
for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) { for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) {
/* Type in parent may be a port, not a global */ /* Type in parent may be a port, not a global */
if (ob2->type == tmp->type || ob2->type == PORT) { if (ob2->type == tmp->type || ob2->type == PORT) {
if ((*matchfunc)(tmp->name, ob2->name)) { if ((*matchfunc)(tmp->name, ob2->name)) {
if (ob2->node >= 0) { if (ob2->node >= 0) {
// Replace all child objects with this node number // Replace all child objects with this node number
rnodenum = tmp->node; rnodenum = tmp->node;
for (ob3 = ChildObjList; ob3 != NULL; ob3 = ob3->next) { for (ob3 = ChildStart; ob3 != NULL; ob3 = ob3->next) {
if (ob3->node == rnodenum) if (ob3->node == rnodenum)
ob3->node = ob2->node; ob3->node = ob2->node;
} }
break; break;
} }
} }
} }
} }
// Don't hash this if the parent had a port of this name // Don't hash this if the parent had a port of this name
if (!ob2 || ob2->type != PORT) if (!ob2 || ob2->type != PORT)
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict)); HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
continue; continue;
} }
#if OLDPREFIX #if OLDPREFIX
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR, sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->name); tmp->name);
#else #else
strcpy(tmpstr+prefixlength,tmp->name); strcpy(tmpstr+prefixlength,tmp->name);
#endif #endif
if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr); if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr);
FreeString(tmp->name); FreeString(tmp->name);
tmp->name = strsave(tmpstr); tmp->name = strsave(tmpstr);
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
#if OLDPREFIX #if OLDPREFIX
sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR, sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR,
tmp->instance.name); tmp->instance.name);
#else #else
strcpy(tmpstr+prefixlength,tmp->instance.name); strcpy(tmpstr+prefixlength,tmp->instance.name);
#endif #endif
FreeString(tmp->instance.name); FreeString(tmp->instance.name);
tmp->instance.name = strsave(tmpstr); tmp->instance.name = strsave(tmpstr);
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict)); if (tmp->type == FIRSTPIN)
if (tmp->type == FIRSTPIN) HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict)); }
}
/* Do property inheritance */
if (ParentProps) {
for (ob2 = ChildObjList; ob2 != NULL; ob2=ob2->next) {
/* If the parent cell has properties to declare, then */
/* pass them on to children. Use globals only if the */
/* spiceparams dictionary is active (during file reading */
/* only). */
if (ob2->type == PROPERTY)
ReduceExpressions(ob2, ParentProps, ChildCell,
(spiceparams.hashtab == NULL) ? 0 : 1);
} }
/* Do property inheritance */
/* NOTE: Need to do: Check properties for M > 1 and decrement
* and repeat without moving CurrentProp
*/
if (CurrentProp) {
for (ob2 = ChildStart; ob2 != NULL; ob2=ob2->next) {
/* If the parent cell has properties to declare, then */
/* pass them on to children. Use globals only if the */
/* spiceparams dictionary is active (during file */
/* reading only). */
if (ob2->type == PROPERTY)
ReduceExpressions(ob2, CurrentProp, ChildCell,
(spiceparams.hashtab == NULL) ? 0 : 1);
}
/* Repeat for each property record, as each property represents a
* unique instance that must be flattened individually.
*/
CurrentProp = CurrentProp->next;
if (CurrentProp->type != PROPERTY) break;
}
else break;
/* Put the child cell at the start of ChildObjList */
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
} }
/* splice instance out of parent */ /* Put the child cell at the start of ChildObjList */
if ((ParentParams == ThisCell->cell) && (ChildObjList == NULL)) { ChildEnd->next = ChildObjList;
ThisCell->cell = ob2; /* Child cell was empty */ ChildObjList = ChildStart;
tmp = ob2;
} /* Pull the instance out of the parent */
else {
if ((ParentParams != ThisCell->cell) || (ChildObjList != NULL)) {
if (ParentParams == ThisCell->cell) { if (ParentParams == ThisCell->cell) {
/* ParentParams are the very first thing in the list */ /* ParentParams are the very first thing in the list */
ThisCell->cell = ChildObjList; ThisCell->cell = ChildObjList;
for (ob2 = ChildObjList; ob2 && ob2->next != NULL; ob2 = ob2->next) ;
} }
else { else {
/* find ParentParams in ThisCell list */ /* find ParentParams in ThisCell list. In most cases, LastObj */
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams; ob2=ob2->next); /* should be pointing to it. */
if (ob2) if (LastObj && (LastObj->next == ParentParams)) {
for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ; LastObj->next = ChildObjList;
}
else {
for (ob2 = LastObj; ob2 && ob2->next != ParentParams;
ob2 = ob2->next);
if (ob2 == NULL) {
/* It should not happen that LastObj is ahead of ParentParams */
/* but just in case, this long loop will find it. */
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams;
ob2 = ob2->next);
}
ob2->next = ChildObjList;
}
} }
/* now, ob2 is last element in child list, so skip and reclaim parent */
tmp = ParentParams; /* Link end of child list into the parent */
do { if (ChildListEnd)
tmp = tmp->next; ChildListEnd->next = ParentEnd->next;
} while ((tmp != NULL) && ((tmp->type > FIRSTPIN) || (tmp->type == PROPERTY)));
if (ob2) ob2->next = tmp;
} }
while (ParentParams != tmp) { while (ParentParams != ChildListEnd->next) {
ob2 = ParentParams->next; ob2 = ParentParams->next;
FreeObjectAndHash(ParentParams, ThisCell); FreeObjectAndHash(ParentParams, ThisCell);
ParentParams = ob2; ParentParams = ob2;
} }
NextObj = ParentParams; NextObj = ParentParams;
} /* repeat until no more instances found */ } /* repeat until no more instances found */
@@ -1136,9 +1201,10 @@ int UniquePins(char *name, int filenum)
struct nlist *cleanuppins(struct hashlist *p, void *clientdata) struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
{ {
struct nlist *ptr; struct nlist *ptr;
struct objlist *ob, *obt, *lob, *nob, *firstpin; struct objlist *ob, *obt, *lob, *nob, *firstpin, *pob;
struct nlist *tc = (struct nlist *)clientdata; struct nlist *tc = (struct nlist *)clientdata;
int pinnum; int pinnum;
char *saveinst = NULL;
ptr = (struct nlist *)(p->ptr); ptr = (struct nlist *)(p->ptr);
if (tc->file != ptr->file) return NULL; if (tc->file != ptr->file) return NULL;
@@ -1186,13 +1252,44 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
} }
FREE(ob->name); FREE(ob->name);
if (ob->instance.name != NULL) FREE(ob->instance.name); if (ob->instance.name != NULL) {
/* Keep a copy of the instance name (see below) */
if (saveinst != NULL) FREE(saveinst);
saveinst = ob->instance.name;
}
if (ob->model.class != NULL) FREE(ob->model.class); if (ob->model.class != NULL) FREE(ob->model.class);
FREE(ob); FREE(ob);
} }
else { else {
lob = ob; if ((ob->type == PROPERTY) && (pinnum == 1))
ob->type = pinnum++; // Renumber pins in order {
/* If this happens, then all the pins got removed,
* and there is probably something very much wrong
* with the setup. However, to keep netgen from
* blowing up, add back a "proxy(no pins)" record
* in front; otherwise we'd have an orphaned
* property record.
*/
pob = GetObject();
pob->name = (char *)MALLOC(15);
sprintf(pob->name, "proxy(no pins)");
pob->model.class = strsave(ob->model.class);
if (saveinst != NULL)
pob->instance.name = strsave(saveinst);
else
/* This should never happen */
pob->instance.name = strsave("error");
pob->type = pinnum++;
pob->node = -1;
pob->next = ob;
lob->next = pob;
lob = ob;
}
else
{
lob = ob;
ob->type = pinnum++; // Renumber pins in order
}
} }
ob = nob; ob = nob;
obt = obt->next; obt = obt->next;
@@ -1203,6 +1300,8 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
HashPtrInstall(firstpin->instance.name, firstpin, &(ptr->instdict)); HashPtrInstall(firstpin->instance.name, firstpin, &(ptr->instdict));
} }
} }
if (saveinst != NULL) FREE(saveinst);
return NULL; /* Keep the search going */ return NULL; /* Keep the search going */
} }
@@ -1475,13 +1574,13 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
} }
} }
if (match) { if (match) {
if (ecomp->cell1) { if (ecomp->cell1 && (ecomp->num1 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s" Fprintf(stdout, "Flattening instances of %s in cell %s"
" makes a better match\n", ecomp->cell1->name, " makes a better match\n", ecomp->cell1->name,
name1); name1);
flattenInstancesOf(name1, file1, ecomp->cell1->name); flattenInstancesOf(name1, file1, ecomp->cell1->name);
} }
if (ecomp->cell2) { if (ecomp->cell2 && (ecomp->num2 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s" Fprintf(stdout, "Flattening instances of %s in cell %s"
" makes a better match\n", ecomp->cell2->name, " makes a better match\n", ecomp->cell2->name,
name2); name2);
@@ -1660,7 +1759,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
while (ecomp != NULL) { while (ecomp != NULL) {
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) && if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
((ecomp->cell1->class == CLASS_RES) || ((ecomp->cell1->class == CLASS_RES) ||
(ecomp->cell1->class == CLASS_VSOURCE))) { (ecomp->cell1->class == CLASS_VSOURCE) ||
(ecomp->cell1->class == CLASS_ISOURCE))) {
int node1 = -1, node2 = -1; int node1 = -1, node2 = -1;
lob = NULL; lob = NULL;
for (ob1 = tc1->cell; ob1; ) { for (ob1 = tc1->cell; ob1; ) {
@@ -1709,11 +1809,16 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
tsub1->name, tsub1->name,
tc1->name); tc1->name);
/* merge node of endpoints */ /* A current source is an open, while a */
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) { /* resistor or voltage source is a short. */
if (ob2->node == node2)
ob2->node = node1; if (ecomp->cell1->class != CLASS_ISOURCE) {
} /* merge node of endpoints */
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
if (ob2->node == node2)
ob2->node = node1;
}
}
/* snip, snip. Excise this device */ /* snip, snip. Excise this device */
if (lob == NULL) { if (lob == NULL) {
@@ -1765,7 +1870,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) && if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) &&
((ecomp->cell2->class == CLASS_RES) || ((ecomp->cell2->class == CLASS_RES) ||
(ecomp->cell2->class == CLASS_VSOURCE))) { (ecomp->cell2->class == CLASS_VSOURCE) ||
(ecomp->cell2->class == CLASS_ISOURCE))) {
int node1 = -1, node2 = -1; int node1 = -1, node2 = -1;
lob = NULL; lob = NULL;
for (ob2 = tc2->cell; ob2; ) { for (ob2 = tc2->cell; ob2; ) {
@@ -1815,9 +1921,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
tc2->name); tc2->name);
/* merge node of endpoints */ /* merge node of endpoints */
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) { if (ecomp->cell2->class != CLASS_ISOURCE) {
if (ob1->node == node2) for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
ob1->node = node1; if (ob1->node == node2)
ob1->node = node1;
}
} }
/* snip, snip. Excise this device */ /* snip, snip. Excise this device */
+611 -363
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -7,7 +7,6 @@ extern struct nlist *Circuit1;
extern struct nlist *Circuit2; extern struct nlist *Circuit2;
extern int ExhaustiveSubdivision; extern int ExhaustiveSubdivision;
extern int FastSymmetryBreaking;
extern int left_col_end; extern int left_col_end;
extern int right_col_end; extern int right_col_end;
+619 -530
View File
File diff suppressed because it is too large Load Diff
+12 -1
View File
@@ -43,7 +43,8 @@ extern int PropertyMerge(char *name, int fnum, char *key, int merge_type,
int merge_mask); int merge_mask);
extern void ResolveProperties(char *name1, int file1, char *name2, int file2); extern void ResolveProperties(char *name1, int file1, char *name2, int file2);
extern void CopyProperties(struct objlist *obj_to, struct objlist *obj_from); extern void CopyProperties(struct objlist *obj_to, struct objlist *obj_from);
extern int PromoteProperty(struct property *, struct valuelist *); extern int PromoteProperty(struct property *, struct valuelist *,
struct objlist *, struct nlist *);
extern int SetPropertyDefault(struct property *, struct valuelist *); extern int SetPropertyDefault(struct property *, struct valuelist *);
extern struct objlist *LinkProperties(char *model, struct keyvalue *topptr); extern struct objlist *LinkProperties(char *model, struct keyvalue *topptr);
extern int ReduceExpressions(struct objlist *instprop, struct objlist *parprops, extern int ReduceExpressions(struct objlist *instprop, struct objlist *parprops,
@@ -65,6 +66,15 @@ extern void AssignCircuits(char *name1, int file1, char *name2, int file2);
/* flatten.c */ /* flatten.c */
extern int PrematchLists(char *, int, char *, int); extern int PrematchLists(char *, int, char *, int);
/* verilog.c */
struct cellstack {
char *cellname;
struct cellstack *next;
};
void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
int blackbox);
/* Define (enumerate) various device classes, largely based on SPICE */ /* Define (enumerate) various device classes, largely based on SPICE */
/* model types, mixed with some ext/sim types. */ /* model types, mixed with some ext/sim types. */
@@ -140,6 +150,7 @@ extern int NoOutput; /* set this to 1 to disable stdout output */
extern int Composition; /* direction of composition */ extern int Composition; /* direction of composition */
extern int UnixWildcards; /* TRUE if *,?,{},[] only; false if full REGEXP */ extern int UnixWildcards; /* TRUE if *,?,{},[] only; false if full REGEXP */
extern int GlobalParallelNone; /* If TRUE, don't parallel combine any cells */ extern int GlobalParallelNone; /* If TRUE, don't parallel combine any cells */
extern int GlobalParallelOpen; /* If TRUE, parallel combine cells w/no-connects */
/* magic internal flag to restrict searches to recently placed cells */ /* magic internal flag to restrict searches to recently placed cells */
extern int QuickSearch; extern int QuickSearch;
/* does re"CellDef"ing a cell add to it or overwrite it??? */ /* does re"CellDef"ing a cell add to it or overwrite it??? */
+6 -3
View File
@@ -975,7 +975,10 @@ struct objlist *CopyObjList(struct objlist *oldlist, unsigned char doforall)
newob->model.class = NULL; newob->model.class = NULL;
else else
newob->model.class = strsave(tmp->model.class); newob->model.class = strsave(tmp->model.class);
newob->instance.name = (tmp->instance.name) ? if (newob->type == NODE)
newob->instance.flags = tmp->instance.flags;
else
newob->instance.name = (tmp->instance.name) ?
strsave(tmp->instance.name) : NULL; strsave(tmp->instance.name) : NULL;
} }
newob->node = tmp->node; newob->node = tmp->node;
@@ -1095,8 +1098,8 @@ void FreeObject(struct objlist *ob)
FREE(ob->instance.props); FREE(ob->instance.props);
} }
} }
else { else if (ob->type != NODE) {
/* All other records */ /* All other records except NODE, which uses this for flags */
if (ob->instance.name != NULL) FreeString(ob->instance.name); if (ob->instance.name != NULL) FreeString(ob->instance.name);
} }
if (ob->model.class != NULL) FreeString(ob->model.class); if (ob->model.class != NULL) FreeString(ob->model.class);
+3
View File
@@ -28,6 +28,8 @@
#define PROXY (0) /* Used in model.port record of ports */ #define PROXY (0) /* Used in model.port record of ports */
#define NO_CONNECT 1 /* Use in object list to flag an isolated net */
/* Lists of device properties. Order is defined at the time of */ /* Lists of device properties. Order is defined at the time of */
/* the cell definition; values are sorted at the time instances */ /* the cell definition; values are sorted at the time instances */
/* are read. */ /* are read. */
@@ -160,6 +162,7 @@ struct objlist {
union { union {
char *name; /* unique name for the instance, or */ char *name; /* unique name for the instance, or */
/* (string) value of property for properties */ /* (string) value of property for properties */
int flags; /* Used by NODE type to flag isolated net */
struct valuelist *props; /* Property record */ struct valuelist *props; /* Property record */
} instance; } instance;
int node; /* the electrical node number of the port/node/pin */ int node; /* the electrical node number of the port/node/pin */
+12 -10
View File
@@ -473,15 +473,6 @@ void CleanupSubcell() {
SetClass(CLASS_MODULE); SetClass(CLASS_MODULE);
} }
/*------------------------------------------------------*/
/* Structure for stacking nested subcircuit definitions */
/*------------------------------------------------------*/
struct cellstack {
char *cellname;
struct cellstack *next;
};
/*------------------------------------------------------*/ /*------------------------------------------------------*/
/* Push a subcircuit name onto the stack */ /* Push a subcircuit name onto the stack */
/*------------------------------------------------------*/ /*------------------------------------------------------*/
@@ -874,6 +865,17 @@ skip_ends:
} }
} }
/* Ignore anything in a .CONTROL ... .ENDC block */
else if (matchnocase(nexttok, ".CONTROL")) {
while (1) {
SpiceSkipNewLine();
SkipTok(NULL);
if (EndParseFile()) break;
if (matchnocase(nexttok, ".ENDC"))
break;
}
}
// Blackbox (library) mode---parse only subcircuits and models; // Blackbox (library) mode---parse only subcircuits and models;
// ignore all components. // ignore all components.
@@ -1184,7 +1186,7 @@ skip_ends:
else if (CountPorts(model, filenum) != 2) { else if (CountPorts(model, filenum) != 2) {
/* Modeled device: Make sure it has the right number of ports */ /* Modeled device: Make sure it has the right number of ports */
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a " Fprintf(stderr, "Device \"%s\" has wrong number of ports for a "
"resistor.\n"); "resistor.\n", model);
goto baddevice; goto baddevice;
} }
usemodel = 1; usemodel = 1;
+90 -5
View File
@@ -63,6 +63,10 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()" #define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}" #define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
// Used by portelement structure "flags" record.
#define PORT_NOT_FOUND 0
#define PORT_FOUND 1
// Global storage for verilog parameters // Global storage for verilog parameters
struct hashdict verilogparams; struct hashdict verilogparams;
// Global storage for verilog definitions // Global storage for verilog definitions
@@ -632,11 +636,6 @@ void CleanupModule() {
/* Structure for stacking nested module definitions */ /* Structure for stacking nested module definitions */
/*------------------------------------------------------*/ /*------------------------------------------------------*/
struct cellstack {
char *cellname;
struct cellstack *next;
};
/* Forward declarations */ /* Forward declarations */
extern void IncludeVerilog(char *, int, struct cellstack **, int); extern void IncludeVerilog(char *, int, struct cellstack **, int);
@@ -1409,6 +1408,7 @@ skip_endmodule:
char *name; // Name of port in subcell char *name; // Name of port in subcell
char *net; // Name of net connecting to port in the parent char *net; // Name of net connecting to port in the parent
int width; // Width of port, if port is a bus int width; // Width of port, if port is a bus
char flags; // Used for marking if port was added into netlist
struct portelement *next; struct portelement *next;
}; };
@@ -1535,6 +1535,7 @@ nextinst:
new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement)); new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement));
new_port->name = strsave(nexttok + 1); new_port->name = strsave(nexttok + 1);
new_port->width = -1; new_port->width = -1;
new_port->flags = PORT_NOT_FOUND;
SkipTokComments(VLOG_DELIMITERS); SkipTokComments(VLOG_DELIMITERS);
if (!match(nexttok, "(")) { if (!match(nexttok, "(")) {
Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok); Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok);
@@ -1969,6 +1970,7 @@ nextinst:
obpinidx = -1; obpinidx = -1;
while (scan != NULL) { while (scan != NULL) {
if (match(obpinname, scan->name)) { if (match(obpinname, scan->name)) {
scan->flags |= PORT_FOUND;
break; break;
} }
scan = scan->next; scan = scan->next;
@@ -2061,6 +2063,89 @@ nextinst:
if (LookupObject(scan->net, CurrentCell) == NULL) Node(scan->net); if (LookupObject(scan->net, CurrentCell) == NULL) Node(scan->net);
join(scan->net, obptr->name); join(scan->net, obptr->name);
} }
/* Before exiting the loop, check if all ports in the */
/* scan list were handled. */
if ((obptr->next == NULL) || (obptr->next->type <= FIRSTPIN)) {
for (scan = head; scan; scan = scan->next) {
if (!(scan->flags & PORT_FOUND)) {
if (tp->flags & CELL_PLACEHOLDER) {
char tempname[128];
int maxnode;
/* 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);
ReopenCellDef((*CellStackPtr)->cellname, filenum);
/* obptr->next now gets the new port. Update the */
/* port number, and copy class and instance name. */
nobj = GetObject();
sprintf(tempname, "%s%s%s", obptr->instance.name,
SEPARATOR, scan->name);
nobj->name = strsave(tempname);
nobj->model.class = strsave(obptr->model.class);
nobj->instance.name = strsave(obptr->instance.name);
nobj->type = obptr->type + 1;
nobj->next = obptr->next;
nobj->node = -1;
obptr->next = nobj;
HashPtrInstall(nobj->name, nobj, &(CurrentCell->objdict));
if (LookupObject(scan->net, CurrentCell) == NULL)
Node(scan->net);
join(scan->net, nobj->name);
scan->flags |= PORT_FOUND;
/* Now any previous instance of the same cell must */
/* insert the same additional pin as a no-connect. */
/* NOTE: This should be running a callback on all */
/* cells in the file, not just CurrentCell. */
for (sobj = CurrentCell->cell; sobj && (sobj != obptr);
sobj = sobj->next) {
if (sobj->type == FIRSTPIN) {
if (match(sobj->model.class, obptr->model.class)) {
while (sobj->next->type > FIRSTPIN)
sobj = sobj->next;
/* Stop when reaching the current instance */
if (sobj->type == obptr->type + 1) break;
nobj = GetObject();
sprintf(tempname, "%s%s%s", sobj->instance.name,
SEPARATOR, scan->name);
nobj->name = strsave(tempname);
nobj->model.class = strsave(sobj->model.class);
nobj->instance.name = strsave(sobj->instance.name);
nobj->type = obptr->type + 1;
nobj->node = -1;
nobj->next = sobj->next;
sobj->next = nobj;
HashPtrInstall(nobj->name, nobj,
&(CurrentCell->objdict));
sprintf(tempname, "_noconnect_%d_", localcount++);
Node(tempname);
join(tempname, nobj->name);
Fprintf(stderr, "Note: Implicit pin %s in instance "
"%s of %s in cell %s\n",
scan->name, sobj->instance.name,
modulename, CurrentCell->name);
}
}
}
}
else {
Fprintf(stderr, "Error: Instance %s has pin %s which is "
"not in the %s cell definition.\n",
locinst, scan->name, modulename);
}
}
}
}
obptr = obptr->next; obptr = obptr->next;
} while (obptr != NULL && obptr->type > FIRSTPIN); } while (obptr != NULL && obptr->type > FIRSTPIN);
} }
+2
View File
@@ -8,6 +8,8 @@ SCRIPTINSTALL = $(DESTDIR)${INSTALL_PYDIR}
include ${NETGENDIR}/defs.mak include ${NETGENDIR}/defs.mak
CLEANS += lvs_manager.py
main: lvs_manager.py main: lvs_manager.py
tcl-main: lvs_manager.py tcl-main: lvs_manager.py
-819
View File
@@ -1,819 +0,0 @@
#!/bin/env python3
#
#--------------------------------------------------------
# LVS Manager GUI.
#
# This is a Python tkinter script that handles the
# process of running LVS and interpreting results.
#
#--------------------------------------------------------
# Written by Tim Edwards
# efabless, inc.
# Version 1. November 30, 2016
# Version 2. March 6, 2017. Reads JSON format output
# Version 3. April 25, 2018. Handles layout vs. verilog
#--------------------------------------------------------
import io
import os
import re
import sys
import json
import shutil
import signal
import socket
import select
import datetime
import contextlib
import subprocess
import tkinter
from tkinter import ttk
from tkinter import filedialog
import tksimpledialog
import tooltip
from consoletext import ConsoleText
from helpwindow import HelpWindow
from treeviewsplit import TreeViewSplit
# User preferences file (if it exists)
prefsfile = '~/.profile/prefs.json'
netgen_script_dir = '/usr/local/lib/netgen/python'
#------------------------------------------------------
# Simple dialog for confirming quit
#------------------------------------------------------
class ConfirmDialog(tksimpledialog.Dialog):
def body(self, master, warning, seed):
if warning:
ttk.Label(master, text=warning, wraplength=500).grid(row = 0, columnspan = 2, sticky = 'wns')
return self
def apply(self):
return 'okay'
#------------------------------------------------------
# Main class for this application
#------------------------------------------------------
class LVSManager(ttk.Frame):
"""LVS Manager GUI."""
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
parent.protocol("WM_DELETE_WINDOW", self.on_quit)
def on_quit(self):
"""Exits program."""
if self.msock:
self.msock.close()
quit()
def init_gui(self):
"""Builds GUI."""
global prefsfile
message = []
fontsize = 11
# Read user preferences file, get default font size from it.
prefspath = os.path.expanduser(prefsfile)
if os.path.exists(prefspath):
with open(prefspath, 'r') as f:
self.prefs = json.load(f)
if 'fontsize' in self.prefs:
fontsize = self.prefs['fontsize']
else:
self.prefs = {}
s = ttk.Style()
available_themes = s.theme_names()
s.theme_use(available_themes[0])
s.configure('bg.TFrame', background='gray40')
s.configure('italic.TLabel', font=('Helvetica', fontsize, 'italic'))
s.configure('title.TLabel', font=('Helvetica', fontsize, 'bold italic'),
foreground = 'brown', anchor = 'center')
s.configure('normal.TLabel', font=('Helvetica', fontsize))
s.configure('red.TLabel', font=('Helvetica', fontsize), foreground = 'red')
s.configure('green.TLabel', font=('Helvetica', fontsize), foreground = 'green3')
s.configure('blue.TLabel', font=('Helvetica', fontsize), foreground = 'blue')
s.configure('normal.TButton', font=('Helvetica', fontsize),
border = 3, relief = 'raised')
s.configure('red.TButton', font=('Helvetica', fontsize), foreground = 'red',
border = 3, relief = 'raised')
s.configure('green.TButton', font=('Helvetica', fontsize), foreground = 'green3',
border = 3, relief = 'raised')
s.configure('blue.TButton', font=('Helvetica', fontsize), foreground = 'blue',
border = 3, relief = 'raised')
s.configure('redtitle.TButton', font=('Helvetica', fontsize, 'bold italic'),
foreground = 'red', border = 3, relief = 'raised')
s.configure('bluetitle.TButton', font=('Helvetica', fontsize, 'bold italic'),
foreground = 'blue', border = 3, relief = 'raised')
# These values to be overridden from arguments
self.rootpath = None
self.project = None
self.logfile = None
self.msock = None
self.help = None
# Create the help window
if os.path.exists(netgen_script_dir + '/netgen_help.txt'):
self.help = HelpWindow(self, fontsize = fontsize)
with io.StringIO() as buf, contextlib.redirect_stdout(buf):
self.help.add_pages_from_file('lvs_help.txt')
message = buf.getvalue()
# Set the help display to the first page
self.help.page(0)
# Variables used by option menus and other stuff
self.project = "(no selection)"
self.layout = "(default)"
self.schematic = "(default)"
self.tech = "(none)"
self.lvs_setup = ''
self.lvsdata = {}
# Root window title
self.root.title('LVS Manager')
self.root.option_add('*tearOff', 'FALSE')
self.pack(side = 'top', fill = 'both', expand = 'true')
pane = tkinter.PanedWindow(self, orient = 'vertical', sashrelief='groove', sashwidth=6)
pane.pack(side = 'top', fill = 'both', expand = 'true')
self.toppane = ttk.Frame(pane)
self.botpane = ttk.Frame(pane)
# Get username
if 'username' in self.prefs:
username = self.prefs['username']
else:
username = os.environ['USER']
# Label with the user
self.toppane.title_frame = ttk.Frame(self.toppane)
self.toppane.title_frame.pack(side = 'top', fill = 'x')
self.toppane.title_frame.title = ttk.Label(self.toppane.title_frame, text='User:', style = 'red.TLabel')
self.toppane.title_frame.user = ttk.Label(self.toppane.title_frame, text=username, style = 'blue.TLabel')
self.toppane.title_frame.title.grid(column=0, row=0, ipadx = 5)
self.toppane.title_frame.user.grid(column=1, row=0, ipadx = 5)
self.toppane.title2_frame = ttk.Frame(self.toppane)
self.toppane.title2_frame.pack(side = 'top', fill = 'x')
self.toppane.title2_frame.project_label = ttk.Label(self.toppane.title2_frame, text="Project:",
style = 'title.TLabel')
self.toppane.title2_frame.project_label.grid(column=0, row=0, ipadx = 5)
# New project select button
self.toppane.title2_frame.project_select = ttk.Button(self.toppane.title2_frame,
text=self.project, style='normal.TButton', command=self.choose_project)
self.toppane.title2_frame.project_select.grid(column=1, row=0, ipadx = 5)
tooltip.ToolTip(self.toppane.title2_frame.project_select,
text = "Select new project")
# Show path to project
self.toppane.title2_frame.path_label = ttk.Label(self.toppane.title2_frame, text=self.project,
style = 'normal.TLabel')
self.toppane.title2_frame.path_label.grid(column=2, row=0, ipadx = 5, padx = 10)
# Show top-level layout cellname with select button. Initial cell name is the top-level cell.
self.toppane.title2_frame.tech_label = ttk.Label(self.toppane.title2_frame, text="Technology setup:",
style = 'title.TLabel')
self.toppane.title2_frame.tech_label.grid(column=3, row=0, ipadx = 5)
self.toppane.title2_frame.tech_select = ttk.Button(self.toppane.title2_frame,
text=self.tech, style='normal.TButton', command=self.choose_tech)
self.toppane.title2_frame.tech_select.grid(column=4, row=0, ipadx = 3, padx = 3)
self.toppane.title2_frame.layout_label = ttk.Label(self.toppane.title2_frame, text="Layout:",
style = 'title.TLabel')
self.toppane.title2_frame.layout_label.grid(column=0, row=1, ipadx = 5)
self.toppane.title2_frame.layout_select = ttk.Button(self.toppane.title2_frame,
text=self.layout, style='normal.TButton', command=self.choose_layout)
self.toppane.title2_frame.layout_select.grid(column=1, row=1, ipadx = 3, padx = 3)
# Show top-level schematic cellname with select button. Initial cell name is the top-level cell.
self.toppane.title2_frame.schem_label = ttk.Label(self.toppane.title2_frame, text="Schematic:",
style = 'title.TLabel')
self.toppane.title2_frame.schem_label.grid(column=3, row=1, ipadx = 5)
self.toppane.title2_frame.schem_select = ttk.Button(self.toppane.title2_frame,
text=self.schematic, style='normal.TButton', command=self.choose_netlist)
self.toppane.title2_frame.schem_select.grid(column=4, row=1, ipadx = 3, padx = 3)
tooltip.ToolTip(self.toppane.title2_frame.project_select,
text = "Select new project")
tooltip.ToolTip(self.toppane.title2_frame.layout_select,
text = "Select a layout subcirucit to compare")
tooltip.ToolTip(self.toppane.title2_frame.schem_select,
text = "Select a schematic subcirucit to compare")
#---------------------------------------------
ttk.Separator(self.toppane, orient='horizontal').pack(side = 'top', fill = 'x')
#---------------------------------------------
# Create listbox of Circuit1 vs. Circuit2 results
height = 10
self.toppane.lvsreport = TreeViewSplit(self.toppane, fontsize = fontsize)
self.toppane.lvsreport.populate("Layout:", [], "Schematic:", [],
[["Run", True, self.run_lvs],
# ["Find", True, self.findrecord]
], height = height)
self.toppane.lvsreport.set_title("Line")
self.toppane.lvsreport.pack(side = 'top', fill = 'both', expand = 'true')
tooltip.ToolTip(self.toppane.lvsreport.get_button(0), text="Run LVS")
#---------------------------------------------
# ttk.Separator(self, orient='horizontal').grid(column=0, row=3, sticky='ew')
#---------------------------------------------
# Add a text window below the project name to capture output. Redirect
# print statements to it.
self.botpane.console = ttk.Frame(self.botpane)
self.botpane.console.pack(side = 'top', fill = 'both', expand = 'true')
self.text_box = ConsoleText(self.botpane.console, wrap='word', height = 4)
self.text_box.pack(side='left', fill='both', expand='true')
console_scrollbar = ttk.Scrollbar(self.botpane.console)
console_scrollbar.pack(side='right', fill='y')
# attach console to scrollbar
self.text_box.config(yscrollcommand = console_scrollbar.set)
console_scrollbar.config(command = self.text_box.yview)
# Add button bar at the bottom of the window
self.botpane.bbar = ttk.Frame(self.botpane)
self.botpane.bbar.pack(side = 'top', fill = 'x')
# Define the "quit" button and action
self.botpane.bbar.quit_button = ttk.Button(self.botpane.bbar, text='Quit', command=self.on_quit,
style = 'normal.TButton')
self.botpane.bbar.quit_button.grid(column=0, row=0, padx = 5)
# Define help button
if self.help:
self.botpane.bbar.help_button = ttk.Button(self.botpane.bbar, text='Help',
command=self.help.open, style = 'normal.TButton')
self.botpane.bbar.help_button.grid(column = 2, row = 0, padx = 5)
tooltip.ToolTip(self.botpane.bbar.help_button, text = "Show help window")
# Add the panes once the internal geometry is known.
pane.add(self.toppane)
pane.add(self.botpane)
pane.paneconfig(self.toppane, stretch='first')
# Redirect stdout and stderr to the console as the last thing to do. . .
# Otherwise errors in the GUI get sucked into the void.
self.stdout = sys.stdout
self.stderr = sys.stderr
sys.stdout = ConsoleText.StdoutRedirector(self.text_box)
sys.stderr = ConsoleText.StderrRedirector(self.text_box)
if message:
print(message)
def logprint(self, message, doflush=False):
if self.logfile:
self.logfile.buffer.write(message.encode('utf-8'))
self.logfile.buffer.write('\n'.encode('utf-8'))
if doflush:
self.logfile.flush()
def printout(self, output):
# Generate output
if not output:
return
outlines = output.splitlines()
for line in outlines:
try:
print(line)
except TypeError:
line = line.decode('utf-8')
pritn(line)
def printwarn(self, output):
# Check output for warning or error
if not output:
return 0
warnrex = re.compile('.*warning', re.IGNORECASE)
errrex = re.compile('.*error', re.IGNORECASE)
errors = 0
outlines = output.splitlines()
for line in outlines:
try:
wmatch = warnrex.match(line)
except TypeError:
line = line.decode('utf-8')
wmatch = warnrex.match(line)
ematch = errrex.match(line)
if ematch:
errors += 1
if ematch or wmatch:
print(line)
return errors
def choose_tech(self):
try:
project_path = self.rootpath
initdirname = self.rootpath + '/tech',
except:
print('Must choose a project first.')
return
techname = filedialog.askopenfilename(multiple=False,
initialdir = initdirname,
filetypes = (("Tcl script", "*.tcl"),("All Files","*.*")),
title = "Choose a netgen technology setup script.")
if techname != '':
print("Selected technology setup script " + techname)
techbase = os.path.split(techname)[1]
self.tech = os.path.splitext(techbase)[0]
self.lvs_setup = techname
self.toppane.title2_frame.layout_select.config(text = self.tech)
def choose_layout(self):
try:
project_path = self.rootpath
initdirname = self.rootpath + '/layout',
except:
print('Must choose a project first.')
return
cellname = filedialog.askopenfilename(multiple=False,
initialdir = initdirname,
filetypes = (("Magic layout", "*.mag"),("All Files","*.*")),
title = "Choose a layout cell to compare.")
if cellname != '':
print("Selected compare cell " + cellname)
cellbase = os.path.split(cellname)[1]
self.layout = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.layout_select.config(text = self.layout)
def choose_netlist(self):
try:
project_path = self.rootpath
initdirname = self.rootpath + '/netlist/' + self.project + '.spi'
except:
print('Must choose a project first.')
return
cellname = filedialog.askopenfilename(multiple=False,
initialdir = initdirname,
filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")),
title = "Choose a netlist to compare.")
if cellname != '':
print("Selected compare cell " + cellname)
cellbase = os.path.split(cellname)[1]
self.schematic = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.schem_select.config(text = self.schematic)
fileext = os.path.splitext(cellbase)[1]
if fileext == '.v':
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
elif fileext == '.sp' or fileext == '.spi' or fileext == '.spice' or fileext == '.spc' or fileext == '.ckt':
self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:')
elif fileext == '.cdl':
self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:')
else:
self.toppane.title2_frame.schem_label.config(text = 'Unknown netlist:')
def choose_project(self):
project = filedialog.askdirectory(initialdir = os.getcwd(),
title = "Find a project.")
if project != '':
print("Selected project " + str(project))
result = self.set_project(project)
def set_project(self, rootpath, project_name=None):
# Check if rootpath is valid. For LVS, there should be subdirectories
# "layout/" and "netlist/" or "verilog/".
haslay = os.path.isdir(rootpath + '/layout')
hasvlog = os.path.isdir(rootpath + '/verilog')
hasnet = os.path.isdir(rootpath + '/netlist')
if not haslay or not (hasvlog or hasnet):
if not haslay:
print("Project path has no layout (/layout) subdirectory.")
if not (hasvlog or hasnet):
print("Project path has no verilog (/verilog), or netlist (/netlist) subdirectory.")
# Continue anyway; assume that netlists will be selected manually
if self.logfile:
self.logfile.close()
self.logfile = None
if not project_name:
project = os.path.split(rootpath)[1]
else:
project = project_name
if self.project != project:
self.rootpath = rootpath
self.project = project
# Clear out old project data
self.toppane.lvsreport.repopulate([], [])
# Close any open logfile.
if self.logfile:
self.logfile.close()
self.logfile = None
# Put new log file called 'lvs.log' in the mag/ subdirectory
self.logfile = open(rootpath + '/layout/lvs.log', 'w')
# Print some initial information to the logfile.
self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'),
doflush=True)
# Update project button
self.toppane.title2_frame.project_select.config(text = self.project)
self.toppane.title2_frame.path_label.config(text = self.rootpath)
# Cell name is the same as project name initially
self.layout = self.project
self.schematic = self.project
self.toppane.title2_frame.layout_select.config(text = self.layout)
self.toppane.title2_frame.schem_select.config(text = self.schematic)
# Update schematic button
if os.path.isfile(rootpath + '/verilog/' + self.schematic + '.v'):
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
else:
self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:')
# If there is a comparison file that post-dates both netlists, load it.
self.check_lvs()
return True
def check_layout_out_of_date(self, spipath, layoutpath):
# Check if a netlist (spipath) is out-of-date relative to the layouts
# (layoutpath). Need to read the netlist and check all of the subcells.
need_capture = False
if not os.path.isfile(spipath):
return True
if os.path.isfile(layoutpath):
spi_statbuf = os.stat(spipath)
lay_statbuf = os.stat(layoutpath)
if spi_statbuf.st_mtime < lay_statbuf.st_mtime:
# netlist exists but is out-of-date
need_capture = True
else:
# only found that the top-level-layout is older than the
# netlist. Now need to read the netlist, find all subcircuits,
# and check those dates, too.
layoutdir = os.path.split(layoutpath)[0]
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
with open(spipath, 'r') as ifile:
duttext = ifile.read()
dutlines = duttext.replace('\n+', ' ').splitlines()
for line in dutlines:
lmatch = subrex.match(line)
if lmatch:
subname = lmatch.group(1)
sublayout = layoutdir + '/' + subname + '.mag'
# subcircuits that cannot be found in the current directory are
# assumed to be library components and therefore never out-of-date.
if os.path.exists(sublayout):
sub_statbuf = os.stat(sublayout)
if spi_statbuf.st_mtime < lay_statbuf.st_mtime:
# netlist exists but is out-of-date
need_capture = True
break
return need_capture
def check_schematic_out_of_date(self, spipath, schempath):
# Check if a netlist (spipath) is out-of-date relative to the schematics
# (schempath). Need to read the netlist and check all of the subcells.
need_capture = False
if not os.path.isfile(spipath):
return True
if os.path.isfile(schempath):
spi_statbuf = os.stat(spipath)
sch_statbuf = os.stat(schempath)
if spi_statbuf.st_mtime < sch_statbuf.st_mtime:
# netlist exists but is out-of-date
need_capture = True
else:
# only found that the top-level-schematic is older than the
# netlist. Now need to read the netlist, find all subcircuits,
# and check those dates, too.
schemdir = os.path.split(schempath)[0]
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
with open(spipath, 'r') as ifile:
duttext = ifile.read()
dutlines = duttext.replace('\n+', ' ').splitlines()
for line in dutlines:
lmatch = subrex.match(line)
if lmatch:
subname = lmatch.group(1)
# NOTE: Electric uses library:cell internally to track libraries,
# and maps the ":" to "__" in the netlist. Not entirely certain that
# the double-underscore uniquely identifies the library:cell. . .
librex = re.compile('(.*)__(.*)', re.IGNORECASE)
lmatch = librex.match(subname)
if lmatch:
elecpath = os.path.split(os.path.split(schempath)[0])[0]
libname = lmatch.group(1)
subschem = elecpath + '/' + libname + '.delib/' + lmatch.group(2) + '.sch'
else:
libname = {}
subschem = schemdir + '/' + subname + '.sch'
# subcircuits that cannot be found in the current directory are
# assumed to be library components and therefore never out-of-date.
if os.path.exists(subschem):
sub_statbuf = os.stat(subschem)
if spi_statbuf.st_mtime < sub_statbuf.st_mtime:
# netlist exists but is out-of-date
need_capture = True
break
# mapping of characters to what's allowed in SPICE makes finding
# the associated schematic file a bit difficult. Requires wild-card
# searching.
elif libname:
restr = lmatch.group(2) + '.sch'
restr = restr.replace('.', '\.')
restr = restr.replace('_', '.')
schrex = re.compile(restr, re.IGNORECASE)
libpath = elecpath + '/' + libname + '.delib'
if os.path.exists(libpath):
liblist = os.listdir(libpath)
for file in liblist:
lmatch = schrex.match(file)
if lmatch:
subschem = libpath + '/' + file
sub_statbuf = os.stat(subschem)
if spi_statbuf.st_mtime < sch_statbuf.st_mtime:
# netlist exists but is out-of-date
need_capture = True
break
return need_capture
def check_lvs(self):
# If both netlists exist, and comp.json is more recent than both, then
# load LVS results from comp.json
project_path = self.rootpath
project_name = self.project
layout_path = project_path + '/layout/' + project_name + '.spc'
net_path = project_path + '/netlist/' + project_name + '.spi'
comp_path = project_path + '/layout/comp.json'
if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path):
magtime = os.stat(layout_path).st_mtime
schemtime = os.stat(net_path).st_mtime
comptime = os.stat(comp_path).st_mtime
if comptime > magtime and comptime > schemtime:
print("Loading LVS results from file.")
self.generate(comp_path)
def generate_layout_netlist(self, layout_path, layout_src, project_path):
# Does layout netlist exist and is it current?
if self.check_layout_out_of_date(layout_path, layout_src):
print('Generating layout netlist.')
self.update_idletasks()
mproc = subprocess.Popen(['magic', '-dnull', '-noconsole',
self.layout], stdin = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, cwd = project_path + '/layout',
universal_newlines = True)
mproc.stdin.write("select top cell\n")
mproc.stdin.write("expand\n")
mproc.stdin.write("extract all\n")
mproc.stdin.write("ext2spice hierarchy on\n")
mproc.stdin.write("ext2spice format ngspice\n")
mproc.stdin.write("ext2spice scale off\n")
mproc.stdin.write("ext2spice renumber off\n")
mproc.stdin.write("ext2spice subcircuit top auto\n")
mproc.stdin.write("ext2spice cthresh infinite\n")
mproc.stdin.write("ext2spice rthresh infinite\n")
mproc.stdin.write("ext2spice blackbox on\n")
mproc.stdin.write("ext2spice -o " + self.layout + ".spi\n")
mproc.stdin.write("quit -noprompt\n")
magicout = mproc.communicate()[0]
self.printwarn(magicout)
if mproc.returncode != 0:
print('Failure to generate new layout netlist.')
return False
# Move .spi netlist to project_dir/netlist/lvs/
shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path)
# Remove extraction files
for file in os.listdir(project_path + '/layout'):
if os.path.splitext(file)[1] == '.ext':
os.remove(project_path + '/layout/' + file)
else:
print('Layout netlist is up-to-date, not regenerating.')
return True
def run_lvs(self, value):
# "value" is ignored (?)
# Check if netlists exist and are current; otherwise create them.
# Then run LVS.
project_path = self.rootpath
project_name = self.project
comp_path = project_path + '/layout/comp.json'
has_vlog = False
vlog_path = project_path + '/verilog/' + project_name + '.v'
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi'
net_path = project_path + '/netlist/schem/' + self.schematic + '.spi'
layout_src = project_path + '/layout/' + self.layout + '.mag'
# Does the setup file exist (this is optional)?
if self.lvs_setup != '' and not os.path.isfile('setup.tcl'):
print('No technology setup file selected.')
# Does schematic netlist exist?
if not os.path.isfile(vlog_path) and not os.path.isfile(net_path):
print('Error: No schematic netlist or verilog netlist.')
return
# Does LVS netlist subdirectory exist?
if not os.path.exists(project_path + '/netlist/lvs'):
os.makedirs(project_path + '/netlist/lvs')
# Does layout netlist exist and is it current?
if not self.generate_layout_netlist(layout_path, layout_src, project_path):
return False
# Final check
if not os.path.isfile(layout_path):
print('Error: No netlist generated from magic.')
return
else:
# Read in netlist and convert commas from [X,Y] arrays to vertical bars
# as something that can be converted back as necessary. ngspice treats
# commas as special characters for some reason.
with open(layout_path) as ifile:
spitext = ifile.read()
# spilines = spitext.replace(',', '|')
# with open(layout_path, 'w') as ofile:
# ofile.write(spilines)
# Check the netlist to see if the cell to match is a subcircuit. If
# not, then assume it is the top level.
is_subckt = False
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
dutlines = spitext.replace('\n+', ' ').splitlines()
for line in dutlines:
lmatch = subrex.match(line)
if lmatch:
subname = lmatch.group(1)
if subname == self.layout:
is_subckt = True
break
if is_subckt:
layout_arg = layout_path + ' ' + self.layout
layout_text = '"' + layout_arg + '"'
else:
layout_arg = layout_path
layout_text = layout_arg
if has_vlog:
schem_arg = vlog_path + ' ' + self.schematic
else:
schem_arg = net_path + ' ' + self.schematic
schem_text = '"' + schem_arg + '"'
# Remove any previous comparison output file
comp_out_path = os.path.splitext(comp_path)[0] + '.out'
if os.path.exists(comp_out_path):
os.remove(comp_out_path)
# Run netgen as subprocess
print('Running: netgen -batch lvs ' + layout_text +
' ' + schem_text + ' ' + self.lvs_setup + ' ' + comp_out_path +
' -json -blackbox')
# Note: Because arguments to subprocess are list items, the {filename cell}
# pair does *not* have to be quoted or braced. Doing so causes a parse
# error.
self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs',
layout_arg, schem_arg,
self.lvs_setup, comp_out_path, '-json', '-blackbox'],
cwd=project_path + '/layout',
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
# This is largely unnecessary as netgen usually runs to completion very quickly.
self.watchclock(comp_path)
def watchclock(self, filename):
if self.lvsproc == None:
return
lvs_status = self.lvsproc.poll()
sresult = select.select([self.lvsproc.stdout, self.lvsproc.stderr], [], [], 0)[0]
if self.lvsproc.stdout in sresult:
outstring = self.lvsproc.stdout.readline().decode().strip()
self.logprint(outstring, doflush=True)
print(outstring)
elif self.lvsproc.stderr in sresult:
errstring = self.lvsproc.stderr.readline().decode().strip()
self.logprint(errstring, doflush = True)
print(errstring, file=sys.stderr)
if lvs_status != None:
print("netgen LVS exited with status " + str(lvs_status))
self.lvsproc = None
if lvs_status != 0:
print('Errors encountered in LVS.')
self.logprint('Errors in LVS, lvs status = ' + str(lvs_status), doflush=True)
# Done; now read comp.json and fill the treeview listbox.
self.generate(filename)
else:
self.after(500, lambda: self.watchclock(filename))
# Generate display from "comp.out" file (json file now preferred)
def generate_orig(self, lvspath):
lefttext = []
righttext = []
print("Reading LVS output file " + lvspath)
if os.path.exists(lvspath):
with open(lvspath, 'r') as ifile:
lvslines = ifile.read().splitlines()
for line in lvslines:
if '|' in line:
# parts = line.split('|')
# lefttext.append(parts[0])
# righttext.append(parts[1])
lefttext.append(line[0:42].strip())
righttext.append(line[44:].strip())
else:
lefttext.append(line)
righttext.append('')
# Populate treeview with text
self.toppane.lvsreport.repopulate(lefttext, righttext)
else:
print("Error: No output file generated from LVS.")
# Generate output from LVS report JSON file comp.json
def generate(self, lvspath):
lefttext = []
righttext = []
print("Reading LVS output file " + lvspath)
if os.path.exists(lvspath):
with open(lvspath, 'r') as ifile:
self.lvsdata = json.load(ifile)
# Populate treeview with text
self.toppane.lvsreport.json_repopulate(self.lvsdata)
else:
print("Error: No output file generated from LVS.")
def findrecord(self, value):
print("Unimplemented function")
def findrecord_test(self, value):
# Check if socket is defined; if not, attempt to open one
if not self.msock:
try:
self.msock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except:
print("No response from layout tool.")
if self.msock:
self.msock.connect(("0.0.0.0", 12946))
self.msock.setblocking(False)
if self.msock:
# Pull name of net or device from 'value'
# This is a test:
self.msock.sendall(b'box 0 0 100 100\r\n')
if __name__ == '__main__':
options = []
arguments = []
for item in sys.argv[1:]:
if item.find('-', 0) == 0:
options.append(item)
else:
arguments.append(item)
root = tkinter.Tk()
app = LVSManager(root)
if arguments:
if len(arguments) >= 2:
app.set_project(arguments[0], project_name=arguments[1])
else:
app.set_project(arguments[0])
root.mainloop()
+108 -36
View File
@@ -343,7 +343,7 @@ class LVSManager(ttk.Frame):
techbase = os.path.split(techname)[1] techbase = os.path.split(techname)[1]
self.tech = os.path.splitext(techbase)[0] self.tech = os.path.splitext(techbase)[0]
self.lvs_setup = techname self.lvs_setup = techname
self.toppane.title2_frame.layout_select.config(text = self.tech) self.toppane.title2_frame.tech_select.config(text = self.tech)
def choose_layout(self): def choose_layout(self):
try: try:
@@ -358,30 +358,37 @@ class LVSManager(ttk.Frame):
title = "Choose a layout cell to compare.") title = "Choose a layout cell to compare.")
if cellname != '': if cellname != '':
print("Selected compare cell " + cellname) print("Selected compare cell " + cellname)
self.layout = cellname
cellbase = os.path.split(cellname)[1] cellbase = os.path.split(cellname)[1]
self.layout = os.path.splitext(cellbase)[0] layoutname = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.layout_select.config(text = self.layout) self.toppane.title2_frame.layout_select.config(text = layoutname)
fileext = os.path.splitext(cellbase)[1]
if fileext == '.mag':
self.toppane.title2_frame.layout_label.config(text = 'Layout:')
else:
self.toppane.title2_frame.layout_label.config(text = 'Layout netlist:')
def choose_netlist(self): def choose_netlist(self):
try: try:
project_path = self.rootpath project_path = self.rootpath
initdirname = self.rootpath + '/netlist/' + self.project + '.spi' initdirname = self.rootpath + '/netlist/' + self.project + '.spice'
except: except:
print('Must choose a project first.') print('Must choose a project first.')
return return
cellname = filedialog.askopenfilename(multiple=False, cellname = filedialog.askopenfilename(multiple=False,
initialdir = initdirname, initialdir = initdirname,
filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")), filetypes = (("Spice netlist", "*.spice"),("Verilog netlist", "*.v"),("All Files","*.*")),
title = "Choose a netlist to compare.") title = "Choose a netlist to compare.")
if cellname != '': if cellname != '':
print("Selected compare cell " + cellname) print("Selected compare cell " + cellname)
self.schematic = cellname
cellbase = os.path.split(cellname)[1] cellbase = os.path.split(cellname)[1]
self.schematic = os.path.splitext(cellbase)[0] schematic_name = os.path.splitext(cellbase)[0]
self.toppane.title2_frame.schem_select.config(text = self.schematic) self.toppane.title2_frame.schem_select.config(text = schematic_name)
fileext = os.path.splitext(cellbase)[1] fileext = os.path.splitext(cellbase)[1]
if fileext == '.v': if fileext == '.v':
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
elif fileext == '.sp' or fileext == '.spi' or fileext == '.spice' or fileext == '.spc' or fileext == '.ckt': elif fileext == '.sp' or fileext == '.spice' or fileext == '.spi' or fileext == '.spc' or fileext == '.ckt':
self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:') self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:')
elif fileext == '.cdl': elif fileext == '.cdl':
self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:') self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:')
@@ -433,7 +440,10 @@ class LVSManager(ttk.Frame):
self.logfile = None self.logfile = None
# Put new log file called 'lvs.log' in the mag/ subdirectory # Put new log file called 'lvs.log' in the mag/ subdirectory
self.logfile = open(rootpath + '/layout/lvs.log', 'w') if os.path.exists(rootpath + '/layout'):
self.logfile = open(rootpath + '/layout/lvs.log', 'w')
else:
self.logfile = open(rootpath + '/lvs.log', 'w')
# Print some initial information to the logfile. # Print some initial information to the logfile.
self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'), self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'),
doflush=True) doflush=True)
@@ -444,15 +454,23 @@ class LVSManager(ttk.Frame):
# Cell name is the same as project name initially # Cell name is the same as project name initially
self.layout = self.project self.layout = self.project
self.schematic = self.project self.schematic = self.project
self.toppane.title2_frame.layout_select.config(text = self.layout) layname = os.path.splitext(os.path.split(self.layout)[1])[0]
self.toppane.title2_frame.schem_select.config(text = self.schematic) self.toppane.title2_frame.layout_select.config(text = layname)
schemname = os.path.splitext(os.path.split(self.schematic)[1])[0]
self.toppane.title2_frame.schem_select.config(text = schemname)
# Update schematic button # Update schematic button
if os.path.isfile(rootpath + '/verilog/' + self.schematic + '.v'): if os.path.splitext(self.schematic)[1] == '.v':
self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:')
else: else:
self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:') self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:')
# Update layout button
if os.path.splitext(self.layout)[1] == '.mag':
self.toppane.title2_frame.schem_label.config(text = 'Layout:')
else:
self.toppane.title2_frame.schem_label.config(text = 'Layout netlist:')
# If there is a comparison file that post-dates both netlists, load it. # If there is a comparison file that post-dates both netlists, load it.
self.check_lvs() self.check_lvs()
return True return True
@@ -567,8 +585,8 @@ class LVSManager(ttk.Frame):
# load LVS results from comp.json # load LVS results from comp.json
project_path = self.rootpath project_path = self.rootpath
project_name = self.project project_name = self.project
layout_path = project_path + '/layout/' + project_name + '.spc' layout_path = project_path + '/layout/' + project_name + '.spice'
net_path = project_path + '/netlist/' + project_name + '.spi' net_path = project_path + '/netlist/' + project_name + '.spice'
comp_path = project_path + '/layout/comp.json' comp_path = project_path + '/layout/comp.json'
if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path): if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path):
@@ -599,7 +617,7 @@ class LVSManager(ttk.Frame):
mproc.stdin.write("ext2spice cthresh infinite\n") mproc.stdin.write("ext2spice cthresh infinite\n")
mproc.stdin.write("ext2spice rthresh infinite\n") mproc.stdin.write("ext2spice rthresh infinite\n")
mproc.stdin.write("ext2spice blackbox on\n") mproc.stdin.write("ext2spice blackbox on\n")
mproc.stdin.write("ext2spice -o " + self.layout + ".spi\n") mproc.stdin.write("ext2spice -o " + self.layout + ".spice\n")
mproc.stdin.write("quit -noprompt\n") mproc.stdin.write("quit -noprompt\n")
magicout = mproc.communicate()[0] magicout = mproc.communicate()[0]
self.printwarn(magicout) self.printwarn(magicout)
@@ -607,8 +625,8 @@ class LVSManager(ttk.Frame):
print('Failure to generate new layout netlist.') print('Failure to generate new layout netlist.')
return False return False
# Move .spi netlist to project_dir/netlist/lvs/ # Move .spice netlist to project_dir/netlist/lvs/
shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path) shutil.move(project_path + '/layout/' + self.layout + '.spice', layout_path)
# Remove extraction files # Remove extraction files
for file in os.listdir(project_path + '/layout'): for file in os.listdir(project_path + '/layout'):
if os.path.splitext(file)[1] == '.ext': if os.path.splitext(file)[1] == '.ext':
@@ -625,17 +643,37 @@ class LVSManager(ttk.Frame):
project_path = self.rootpath project_path = self.rootpath
project_name = self.project project_name = self.project
comp_path = project_path + '/layout/comp.json'
# Diagnostic:
print('project_name is ' + project_name)
print('project_path is ' + project_path)
print('self.layout is ' + self.layout)
print('self.schematic is ' + self.schematic)
print('self.lvs_setup is ' + self.lvs_setup)
has_vlog = False has_vlog = False
vlog_path = project_path + '/verilog/' + project_name + '.v' vlog_path = project_path + '/verilog/' + project_name + '.v'
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi' if os.path.isfile(self.layout):
net_path = project_path + '/netlist/schem/' + self.schematic + '.spi' layout_path = self.layout
layout_src = project_path + '/layout/' + self.layout + '.mag' layout_src = None
else:
layout_path = project_path + '/netlist/lvs/' + self.layout + '.spice'
layout_src = project_path + '/layout/' + self.layout + '.mag'
comp_dir = os.path.split(layout_path)[0]
comp_path = comp_dir + '/comp.json'
if os.path.isfile(self.schematic):
net_path = self.schematic
else:
net_path = project_path + '/netlist/schem/' + self.schematic + '.spice'
# Does the setup file exist (this is optional)? # Does the setup file exist (this is optional)?
if self.lvs_setup != '' and not os.path.isfile('setup.tcl'): if self.lvs_setup == '' and not os.path.isfile('setup.tcl'):
print('No technology setup file selected.') print('No technology setup file selected.')
elif not os.path.isfile(self.lvs_setup):
print("Can't find technology setup file " + self.lvs_setup)
# Does schematic netlist exist? # Does schematic netlist exist?
if not os.path.isfile(vlog_path) and not os.path.isfile(net_path): if not os.path.isfile(vlog_path) and not os.path.isfile(net_path):
@@ -643,14 +681,16 @@ class LVSManager(ttk.Frame):
return return
# Does LVS netlist subdirectory exist? # Does LVS netlist subdirectory exist?
if not os.path.exists(project_path + '/netlist/lvs'): if os.path.exists(project_path + '/netlist'):
os.makedirs(project_path + '/netlist/lvs') if not os.path.exists(project_path + '/netlist/lvs'):
os.makedirs(project_path + '/netlist/lvs')
# Does layout netlist exist and is it current? # Does layout netlist exist and is it current?
if not self.generate_layout_netlist(layout_path, layout_src, project_path): if layout_src:
return False if not self.generate_layout_netlist(layout_path, layout_src, project_path):
return False
# Final check # Final checks
if not os.path.isfile(layout_path): if not os.path.isfile(layout_path):
print('Error: No netlist generated from magic.') print('Error: No netlist generated from magic.')
return return
@@ -662,26 +702,27 @@ class LVSManager(ttk.Frame):
with open(layout_path) as ifile: with open(layout_path) as ifile:
spitext = ifile.read() spitext = ifile.read()
# spilines = spitext.replace(',', '|')
# with open(layout_path, 'w') as ofile:
# ofile.write(spilines)
# Check the netlist to see if the cell to match is a subcircuit. If # Check the netlist to see if the cell to match is a subcircuit. If
# not, then assume it is the top level. # not, then assume it is the top level.
layoutcell = os.path.splitext(os.path.split(self.layout)[1])[0]
is_subckt = False is_subckt = False
subname = None
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
dutlines = spitext.replace('\n+', ' ').splitlines() dutlines = spitext.replace('\n+', ' ').splitlines()
for line in dutlines: for line in dutlines:
lmatch = subrex.match(line) lmatch = subrex.match(line)
if lmatch: if lmatch:
subname = lmatch.group(1) subname = lmatch.group(1)
if subname == self.layout: if subname == layoutcell:
is_subckt = True is_subckt = True
break break
if is_subckt: if is_subckt:
layout_arg = layout_path + ' ' + self.layout layout_arg = self.layout + ' ' + layoutcell
layout_text = '"' + layout_arg + '"'
elif subname:
layout_arg = self.layout + ' ' + subname
layout_text = '"' + layout_arg + '"' layout_text = '"' + layout_arg + '"'
else: else:
layout_arg = layout_path layout_arg = layout_path
@@ -690,8 +731,39 @@ class LVSManager(ttk.Frame):
if has_vlog: if has_vlog:
schem_arg = vlog_path + ' ' + self.schematic schem_arg = vlog_path + ' ' + self.schematic
else: else:
schem_arg = net_path + ' ' + self.schematic # Final checks
schem_text = '"' + schem_arg + '"' if not os.path.isfile(net_path):
print('Error: No netlist from schematic.')
return
with open(net_path) as ifile:
spitext = ifile.read()
# Check the netlist to see if the cell to match is a subcircuit. If
# not, then assume it is the top level.
schemcell = os.path.splitext(os.path.split(self.schematic)[1])[0]
subname = None
is_subckt = False
subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE)
dutlines = spitext.replace('\n+', ' ').splitlines()
for line in dutlines:
lmatch = subrex.match(line)
if lmatch:
subname = lmatch.group(1)
if subname == schemcell:
is_subckt = True
break
if is_subckt:
schem_arg = self.schematic + ' ' + schemcell
schem_text = '"' + schem_arg + '"'
elif subname:
schem_arg = self.schematic + ' ' + subname
schem_text = '"' + schem_arg + '"'
else:
schem_arg = net_path
schem_text = schem_arg
# Remove any previous comparison output file # Remove any previous comparison output file
comp_out_path = os.path.splitext(comp_path)[0] + '.out' comp_out_path = os.path.splitext(comp_path)[0] + '.out'
@@ -708,7 +780,7 @@ class LVSManager(ttk.Frame):
self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs', self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs',
layout_arg, schem_arg, layout_arg, schem_arg,
self.lvs_setup, comp_out_path, '-json', '-blackbox'], self.lvs_setup, comp_out_path, '-json', '-blackbox'],
cwd=project_path + '/layout', cwd=comp_dir,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
# This is largely unnecessary as netgen usually runs to completion very quickly. # This is largely unnecessary as netgen usually runs to completion very quickly.
self.watchclock(comp_path) self.watchclock(comp_path)
+89 -14
View File
@@ -352,7 +352,7 @@ proc netgen::convert_to_json {filename lvs_final} {
close $fjson close $fjson
} }
#---------------------------------------------------------------- #-----------------------------------------------------------------------
# Define the "lvs" command as a way of calling the netgen options # Define the "lvs" command as a way of calling the netgen options
# for standard compare, essentially the same as the old "netcomp" # for standard compare, essentially the same as the old "netcomp"
# standalone program. # standalone program.
@@ -361,14 +361,20 @@ proc netgen::convert_to_json {filename lvs_final} {
# although if the cells have not been read in yet, then the # although if the cells have not been read in yet, then the
# original syntax of filename or {filename cellname} is required. # original syntax of filename or {filename cellname} is required.
# #
# "args" is passed to verify and may therefore contain only the # "args" may be "-list", "-json", or "-blackbox".
# value "-list" or nothing. If "-list", then output is returned # "-list" returns output as a nested list.
# as a nested list. # "-json" creates a .json-format output file in addition to stdout.
#---------------------------------------------------------------- # "-blackbox" treats empty cells as black-box entries.
# "-noflatten={list}" is a list of cells not to flatten if mismatched.
# i.e., the cells are expected to match and any mismatch cannot be
# expected to be resolved by flattening the contents of the mismatched
# cells.
#-----------------------------------------------------------------------
proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} { proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
set dolist 0 set dolist 0
set dojson 0 set dojson 0
set noflat {}
foreach arg $args { foreach arg $args {
if {$arg == "-list"} { if {$arg == "-list"} {
puts stdout "Generating list result" puts stdout "Generating list result"
@@ -382,9 +388,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
} elseif {$arg == "-blackbox"} { } elseif {$arg == "-blackbox"} {
puts stdout "Treating empty subcircuits as black-box cells" puts stdout "Treating empty subcircuits as black-box cells"
netgen::model blackbox on netgen::model blackbox on
} elseif {$arg == "-full"} { } elseif {[string first "-noflatten=" $arg] == 0} {
puts stdout "Using full symmetry breaking method" set value [string range $arg 11 end]
netgen::symmetry full # If argument is a filename then read the list of cells from it;
# otherwise, argument is the list of files itself in quotes or
# braces.
if {![catch {file exists $value}]} {
if {![catch {open $value r} fnf]} {
while {[gets $fnf line] >= 0} {
if {[lindex $line 0] != "#"} {
foreach cell $line {
lappend noflat $cell
}
}
}
close $fnf
} else {
puts stderr "Cannot open file $value for reading cell list."
}
} else {
set noflat [string trim $value \"\{\}]
}
if {[llength $noflat] > 0} {
puts stdout "Will not flatten these subcells: $noflat"
}
} }
} }
@@ -504,6 +531,7 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
return return
} }
set properr {} set properr {}
set matcherr {}
set pinsgood 0 set pinsgood 0
while {$endval != {}} { while {$endval != {}} {
if {$dolist == 1} { if {$dolist == 1} {
@@ -532,9 +560,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
# Flatten the non-matching subcircuit (but not the top-level cells) # Flatten the non-matching subcircuit (but not the top-level cells)
if {[netgen::print queue] != {}} { if {[netgen::print queue] != {}} {
netgen::log put " Flattening non-matched subcircuits $endval" if {([lsearch $noflat [lindex $endval 0]] == -1) &&
netgen::flatten class "[lindex $endval 0] $fnum1" ([lsearch $noflat [lindex $endval 1]] == -1)} {
netgen::flatten class "[lindex $endval 1] $fnum2" netgen::log put " Flattening non-matched subcircuits $endval"
netgen::flatten class "[lindex $endval 0] $fnum1"
netgen::flatten class "[lindex $endval 1] $fnum2"
} else {
netgen::log put " Continuing with black-boxed subcircuits $endval"
lappend matcherr [lindex $endval 0]
# Match pins
netgen::log echo off
if {$dolist == 1} {
set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
} else {
set result [equate -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
}
if {$result != 0} {
equate classes "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"
}
set pinsgood $result
netgen::log echo on
}
} }
} else { } else {
# Match pins # Match pins
@@ -557,9 +606,32 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
} else { } else {
# Flatten the non-matching subcircuit (but not the top-level cells) # Flatten the non-matching subcircuit (but not the top-level cells)
if {[netgen::print queue] != {}} { if {[netgen::print queue] != {}} {
netgen::log put " Flattening non-matched subcircuits $endval" if {([lsearch $noflat [lindex $endval 1]] == -1) &&
netgen::flatten class "[lindex $endval 0] $fnum1" ([lsearch $noflat [lindex $endval 1]] == -1)} {
netgen::flatten class "[lindex $endval 1] $fnum2" netgen::log put " Flattening non-matched subcircuits $endval"
netgen::flatten class "[lindex $endval 0] $fnum1"
netgen::flatten class "[lindex $endval 1] $fnum2"
} else {
netgen::log put " Continuing with black-boxed subcircuits $endval"
lappend matcherr [lindex $endval 0]
netgen::log put " Continuing with black-boxed subcircuits $endval"
lappend matcherr [lindex $endval 0]
# Match pins
netgen::log echo off
if {$dolist == 1} {
set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
} else {
set result [equate -force pins "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"]
}
if {$result != 0} {
equate classes "$fnum1 [lindex $endval 0]" \
"$fnum2 [lindex $endval 1]"
}
set pinsgood $result
netgen::log echo on
}
} }
} }
netgen::log echo off netgen::log echo off
@@ -582,6 +654,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
if {$properr != {}} { if {$properr != {}} {
netgen::log put "The following cells had property errors: $properr\n" netgen::log put "The following cells had property errors: $properr\n"
} }
if {$matcherr != {}} {
netgen::log put "The following subcells failed to match: $matcherr\n"
}
if {$dolog} { if {$dolog} {
netgen::log end netgen::log end
} }
+49 -41
View File
@@ -246,8 +246,7 @@ Command netcmp_cmds[] = {
"\n " "\n "
"toggle exhaustive subdivision"}, "toggle exhaustive subdivision"},
{"symmetry", _netcmp_symmetry, {"symmetry", _netcmp_symmetry,
"[fast|full]\n " "(deprecated)"},
"apply method for symmetry breaking"},
{"restart", _netcmp_restart, {"restart", _netcmp_restart,
"\n " "\n "
"start over (reset data structures)"}, "start over (reset data structures)"},
@@ -2458,8 +2457,7 @@ _netcmp_run(ClientData clientData,
automorphisms = ResolveAutomorphsByProperty(); automorphisms = ResolveAutomorphsByProperty();
if (automorphisms == 0) if (automorphisms == 0)
Fprintf(stdout, "Netlists match uniquely.\n"); Fprintf(stdout, "Netlists match uniquely.\n");
else { else if (automorphisms > 0) {
// Next, attempt to resolve automorphisms uniquely by // Next, attempt to resolve automorphisms uniquely by
// using the pin names // using the pin names
automorphisms = ResolveAutomorphsByPin(); automorphisms = ResolveAutomorphsByPin();
@@ -2467,14 +2465,17 @@ _netcmp_run(ClientData clientData,
if (automorphisms == 0) if (automorphisms == 0)
Fprintf(stdout, "Netlists match uniquely.\n"); Fprintf(stdout, "Netlists match uniquely.\n");
else else if (automorphisms > 0) {
// Anything left is truly indistinguishable // Anything left is truly indistinguishable
Fprintf(stdout, "Netlists match with %d symmetr%s.\n", Fprintf(stdout, "Netlists match with %d symmetr%s.\n",
automorphisms, (automorphisms == 1) ? "y" : "ies"); automorphisms, (automorphisms == 1) ? "y" : "ies");
while ((automorphisms = ResolveAutomorphisms()) > 0); while ((automorphisms = ResolveAutomorphisms()) > 0);
if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); }
else Fprintf(stdout, "Circuits match correctly.\n"); if (automorphisms == -1)
Fprintf(stdout, "Netlists do not match.\n");
else
Fprintf(stdout, "Circuits match correctly.\n");
} }
if (PropertyErrorDetected) { if (PropertyErrorDetected) {
Fprintf(stdout, "There were property errors.\n"); Fprintf(stdout, "There were property errors.\n");
@@ -2828,11 +2829,13 @@ _netcmp_equate(ClientData clientData,
char *name1 = NULL, *name2 = NULL, *optstart; char *name1 = NULL, *name2 = NULL, *optstart;
struct nlist *tp1, *tp2, *SaveC1, *SaveC2; struct nlist *tp1, *tp2, *SaveC1, *SaveC2;
struct objlist *ob1, *ob2; struct objlist *ob1, *ob2;
struct ElementClass *saveEclass = NULL;
struct NodeClass *saveNclass = NULL;
int file1, file2; int file1, file2;
int i, l1, l2, ltest, lent, dolist = 0; int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
Tcl_Obj *tobj1, *tobj2, *tobj3; Tcl_Obj *tobj1, *tobj2, *tobj3;
if (objc > 1) { while (objc > 1) {
optstart = Tcl_GetString(objv[1]); optstart = Tcl_GetString(objv[1]);
if (*optstart == '-') optstart++; if (*optstart == '-') optstart++;
if (!strcmp(optstart, "list")) { if (!strcmp(optstart, "list")) {
@@ -2840,6 +2843,13 @@ _netcmp_equate(ClientData clientData,
objv++; objv++;
objc--; objc--;
} }
else if (!strcmp(optstart, "force")) {
doforce = 1;
objv++;
objc--;
}
else
break;
} }
if ((objc != 2) && (objc != 4) && (objc != 6)) { if ((objc != 2) && (objc != 4) && (objc != 6)) {
@@ -2997,6 +3007,12 @@ _netcmp_equate(ClientData clientData,
break; break;
case PINS_IDX: case PINS_IDX:
if ((ElementClasses != NULL) && (doforce == TRUE)) {
saveEclass = ElementClasses;
saveNclass = NodeClasses;
ElementClasses = NULL;
NodeClasses = NULL;
}
if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) { if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) {
if (CurrentCell == NULL) { if (CurrentCell == NULL) {
Fprintf(stderr, "Equate elements: no current cell.\n"); Fprintf(stderr, "Equate elements: no current cell.\n");
@@ -3059,6 +3075,12 @@ _netcmp_equate(ClientData clientData,
/* Recover temporarily set global variables (see above) */ /* Recover temporarily set global variables (see above) */
Circuit1 = SaveC1; Circuit1 = SaveC1;
Circuit2 = SaveC2; Circuit2 = SaveC2;
/* Recover ElementClasses if forcing pins on mismatched circuits */
if (doforce == TRUE) {
ElementClasses = saveEclass;
NodeClasses = saveNclass;
}
} }
break; break;
@@ -3321,10 +3343,10 @@ _netcmp_property(ClientData clientData,
int result, index, idx2; int result, index, idx2;
char *suboptions[] = { char *suboptions[] = {
"integer", "double", "value", "string", NULL "integer", "double", "value", "string", "expression", NULL
}; };
enum SubOptionIdx { enum SubOptionIdx {
INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX, EXPRESSION_IDX
}; };
/* Note: "merge" has been deprecated, but kept for backwards compatibility. */ /* Note: "merge" has been deprecated, but kept for backwards compatibility. */
@@ -3418,9 +3440,15 @@ _netcmp_property(ClientData clientData,
GlobalParallelNone = FALSE; GlobalParallelNone = FALSE;
SetParallelCombine(TRUE); SetParallelCombine(TRUE);
} }
else if (!strcmp(Tcl_GetString(objv[2]), "connected")) {
GlobalParallelOpen = FALSE;
}
else if (!strcmp(Tcl_GetString(objv[2]), "open")) {
GlobalParallelOpen = TRUE;
}
else { else {
Tcl_SetResult(interp, "Bad option, should be property parallel none|all", Tcl_SetResult(interp, "Bad option, should be property parallel "
NULL); "none|all|connected", NULL);
return TCL_ERROR; return TCL_ERROR;
} }
return TCL_OK; return TCL_OK;
@@ -3707,6 +3735,11 @@ _netcmp_property(ClientData clientData,
PropertyString(tp->name, fnum, PropertyString(tp->name, fnum,
Tcl_GetString(tobj1), ival, NULL); Tcl_GetString(tobj1), ival, NULL);
break; break;
case EXPRESSION_IDX:
PropertyString(tp->name, fnum,
Tcl_GetString(tobj1), 0,
Tcl_GetString(tobj3));
break;
} }
break; break;
} }
@@ -3982,39 +4015,14 @@ _netcmp_permute(ClientData clientData,
/* Formerly: x */ /* Formerly: x */
/* Results: */ /* Results: */
/* Side Effects: */ /* Side Effects: */
/* Notes: Deprecated, retained for compatibility. */
/*------------------------------------------------------*/ /*------------------------------------------------------*/
int int
_netcmp_symmetry(ClientData clientData, _netcmp_symmetry(ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{ {
char *fastfull[] = { Printf("Symmetry breaking method has been deprecated.\n");
"fast", "full", NULL
};
enum OptionIdx {
FAST_IDX, FULL_IDX
};
int result, index;
if (objc == 1)
index = -1;
else {
if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)fastfull,
"option", 0, &index) != TCL_OK)
return TCL_ERROR;
}
switch(index) {
case FAST_IDX:
FastSymmetryBreaking = TRUE;
break;
case FULL_IDX:
FastSymmetryBreaking = FALSE;
break;
}
Printf("Symmetry breaking method: %s.\n",
FastSymmetryBreaking ? "FAST" : "FULL");
return TCL_OK; return TCL_OK;
} }