Compare commits

..
139 Commits
Author SHA1 Message Date
Tim Edwards 7cbf15aab1 Merge branch 'master' into netgen-1.5 2025-10-03 02:00:02 -04:00
R. Timothy Edwards b59196fa81 Modified the SPICE file read routine to accept the CDL syntax
"*.GLOBAL" as equivalent to ".GLOBAL".  Corrected the property
matching to handle property combination when no "critical"
property is given.  Critical properties exist when one property
must remain constant and equal for other properties to combine,
such as transistor length.  But, for example, capacitors can
combine area without any restriction based on another property.
Also, corrected the property matching code to allow more than
one property to be additive (example:  capacitor area and
perimeter).  Corrected the equation for adding properties in
parallel combination.
2025-10-02 12:33:28 -04:00
Tim Edwards 1de6f88f1e Merge branch 'master' into netgen-1.5 2025-09-10 02:00:02 -04:00
R. Timothy Edwards 6e6e9fb73f Added code to catch and print an error in connectivity between a
port and an internal node which can be missed when pin permutations
are present.  Previously, that could produce a situation where
netgen would report a "port error" but otherwise list all ports
as matching.  Because the permutation handling makes this hard to
detect while generating pin correspondence output, the non-matching
pins are listed separately at the end, and only if no mismatch was
detected during output.
2025-09-09 13:45:29 -04:00
Tim Edwards 33fed391fd Merge branch 'master' into netgen-1.5 2025-09-01 02:00:02 -04:00
R. Timothy Edwards e84700a607 Added a NULL check at one point in the SPICE read routine that
prevents a segfault under some condition (not fully investigated)
involving .include files.  Appears to resolve the problem without
any unintended consequences.
2025-08-31 16:52:35 -04:00
Tim Edwards 5f5248b3d0 Merge branch 'master' into netgen-1.5 2025-08-27 02:00:03 -04:00
R. Timothy Edwards 0bee21ccc8 Corrected an issue in which a property error in a subcell would not
be reported at the end if there was a port error.  This is important
because port errors often resolve themselves, but the cell should not
be reported clean if the port errors resolved but it had property
errors.  Also:  Added a method to derive area and/or perimeter
properties from length and width, so that capacitors can be combined
in parallel without regard to which dimension is width and which is
length.  This feature has only been lightly tested.
2025-08-26 17:47:46 -04:00
Tim Edwards 80f9263004 Merge branch 'master' into netgen-1.5 2025-08-26 02:00:02 -04:00
R. Timothy Edwards c269f1de89 Corrected an unexpected corner-case error in which if a newline in
a spice netlist falls exactly on the last non-null position of the
input buffer after the buffer has been expanded to accept more
input data, then the next line gets read in automatically, and
the newline gets treated as whitespace and not a newline.
2025-08-25 10:31:19 -04:00
Tim Edwards edb50746cb Merge branch 'master' into netgen-1.5 2025-08-19 02:00:02 -04:00
R. Timothy Edwards 4443826f9e Corrected a place in netcmp.c where a new instance net connection
is created without setting the cell name or instance name.  That
can cause a crash condition when attempting to locate the instance
from the net record.
2025-08-18 10:37:36 -04:00
Tim Edwards f2368ca223 Merge branch 'master' into netgen-1.5 2025-05-18 02:00:02 -04:00
R. Timothy Edwards a60dac6124 Modified the primary SPICE token reading routine so that the call
to strdtok() can differentiate between reading verilog and reading
SPICE.  Otherwise, SPICE containing the (dubious) syntax of using
backslashes in names will get treated as a verilog name with
verilog backslash notation, with generally undesirable results.
When called from the SPICE reading routine, backslashes are
treated as-is and not as verilog notation.
2025-05-17 20:29:38 -04:00
Tim Edwards ee93d52a26 Merge branch 'master' into netgen-1.5 2025-03-26 02:00:02 -04:00
R. Timothy Edwards bbe645f0ab Corrected an error in which netgen was trying to reduce an
expression in a property that was not necessarily a parameter,
and if it wasn't, then netgen would crash.  Surfaced by an
example using complicated parameters that netgen was apparently
unable to handle (an issue for another day;  the main goal here
was to avoid a segmentation violation).
2025-03-25 17:00:58 -04:00
Tim Edwards ba7004fd5b Merge branch 'master' into netgen-1.5 2025-03-10 02:00:02 -04:00
Tim Edwards 4f315d33d6 Fixed a corner case found by Sylvain Munaut (see github issue
tracker #96) in which a subcircuit with only one port (in this
case, a pad) but which has properties (in this case, "M") will
fail to set the pointer position ahead of the property because
the loop starts after the first pin, so it has already missed
the position that needs to be saved.  Fixed by initializing
the value to the first pin position before starting the loop.
2025-03-09 11:07:58 -04:00
Tim Edwards 704bfbc871 Merge branch 'master' into netgen-1.5 2025-02-10 02:00:03 -05:00
Tim Edwards 4457248ecd Corrected a long-standing issue with permutation, which turned out
to be caused by failing to have a systematic way of determining
which pin's hash value would be used for the hash value of all the
pins.  Because equivalent cells in the two netlists may have pins in
different order, it was possible that they might end up with
different hashes.  This was solved simply by always taking the
larger hash value of the two pins belonging to the permutable pair.
Now permutation works correctly for arbitrary subcircuits.
(Previously it worked for low-level components like MOSFETs because
the pin order is always the same.)
2025-02-09 21:26:54 -05:00
Tim Edwards 7bee1851fa Merge branch 'master' into netgen-1.5 2025-01-05 02:00:04 -05:00
Tim Edwards 021dfa6e8a Made changes to tkcon.tcl to ensure compatibility with Tcl version 9. 2025-01-04 14:18:21 -05:00
Tim Edwards e4a4621b96 Merge branch 'master' into netgen-1.5 2025-01-02 02:00:01 -05:00
Tim Edwards 1d286f9973 Corrected an issue with generating proxy pins that had previously
forced flattening to be done whenever any pin mismatch occurred,
which undermined the whole proxy pin method.  With the proxy pins
fixed, reinstated the method of avoiding flattening when pin
issues can be trivially corrected.  Also:  Added output to the
pin matching for one mismatch case that was being missed.
2025-01-01 13:27:39 -05:00
Tim Edwards 3ca77300ac Merge branch 'master' into netgen-1.5 2024-12-28 02:00:02 -05:00
Tim Edwards 6d2ef396ef After giving the previous code change some more thought, I
decided that it is beneficial to break symmetries by net name;
it's just that net names should not be used before all symmetries
related to pins have been broken.  So I rewrote the compare
routine to take an argument allowing or disallowing net name
matches, and make one call to break symmetries by pin name
followed by another call to break symmetries by net name.  This
still solves the original problem, but does not allow symmetries
to be broken randomly on internal nets if names have been matched
in both netlists.  Otherwise the output may report nets that
appear to be swapped, making the output confusing.
2024-12-27 16:20:00 -05:00
Tim Edwards 2483b7440f Corrected an error in "ResolveAutomorphsByPin" where the code states
to check that the nodes with matching names are pins, but never does.
This results in an attempt to resolve automorphs by matching pin
names AND net names.  However, net names can match without the nets
matching, as pointed out by Andrey Bondar (private communication).
Fixed simply by adding the specified check that the node being name-
matched is actually a pin.
2024-12-26 21:20:24 -05:00
Tim Edwards 236fba18aa Merge branch 'master' into netgen-1.5 2024-11-15 02:00:04 -05:00
Tim Edwards 49c0de0433 Corrected an error found by Sylvain Munaut and discussed on
open-source-silicon slack on Nov. 3 in which the simple verilog
expression "assign name1 = name2[a:b]";  this revealed an error
where the parsing of "name2" was being incorrectly run with
GetBusTok() which must be called when the token starts with "[".
This problem existed both for the left-hand-side parsing and
the right-hand-side parsing, and has been fixed for both (where
either side may be a subset of a bus and the other a complete
bus).
2024-11-14 21:28:51 -05:00
Tim Edwards 3b9dca0cf2 Implemented the patch from Sylvain Munaut in github PR#90 (issue
that the position in the code has shifted quite a bit and I
don't really trust that git will do a clean merge.
2024-11-14 20:39:19 -05:00
Tim Edwards 1272ed22fe Merge branch 'master' into netgen-1.5 2024-10-20 02:00:02 -04:00
Tim Edwards 7d910b616c Modified the string matching "matchnocase()" routine to compare
a verilog escaped string against an equivalent non-escaped
string (requires that the escaped string differs from the non-
escaped string by having a "\" at the front and " " at the end.
The space character is always maintained as part of the string).
2024-10-19 17:07:09 -04:00
Tim Edwards 6179ba8cb8 Merge branch 'master' into netgen-1.5 2024-10-17 02:00:02 -04:00
Tim Edwards b1032f846b Refactored code in netcmp.c involved in printing side-by-side
formatted output to make it much cleaner and easier to read.  This
is in preparation of correcting the circuit1<-->circuit2 asymmetry
in the MatchPins() routine.
2024-10-16 20:38:44 -04:00
Tim Edwards 4c546d1472 Corrected an error that prevents property errors from being
printed in detail if a port error is also found.
2024-10-16 09:44:48 -04:00
Tim Edwards aaf8fefc1a Merge branch 'master' into netgen-1.5 2024-10-16 02:00:02 -04:00
Tim Edwards e1aa231db1 Corrected another error discovered by Andrei Bondar in which
the critical property (e.g., L for transistors) is required to
match exactly between devices in order to allow the additive
property (e.g., W for transistors) to be summed.  The critical
property should match if all values are within the slop value,
for floating-point values.  Note:  The implementation is still
not rigorous, as the saved critical value may shift from
device to device;  so comparing, e.g., 1.00 to 1.01 to 1.02 to
1.03, etc., can find that all individual comparisons are within
the slop value even though the slop is exceeded across all values.
2024-10-15 20:52:23 -04:00
Tim Edwards abaf896f7f Merge branch 'master' into netgen-1.5 2024-10-15 02:00:02 -04:00
Tim Edwards df8fa29b2f Fixed an issue with property matching that was preventing the last-
ditch effort of matching based on combining devices with the same
critical property (e.g., adding gate widths together for transistors
of the same gate length, if the property records remain stubbornly
mismatched to the end).  Thanks to Bondar Andrey Renatovich for
surfacing this issue and providing a reproducible example.
2024-10-14 13:24:35 -04:00
Tim Edwards e94d25b3f1 Merge branch 'master' into netgen-1.5 2024-10-08 02:00:03 -04:00
Tim Edwards d14bf70f1c Working to get some MatchPins improvements from Mitch Bailey from
a long time ago into the code.  The improvements collided with
intervening changes to the same routines and would not merge
cleanly, which is why they were never merged.  Step 1:  Show the
net name of a matching net that is missing a pin.  Remove output
of missing pins that is redundant (pin names being output twice).
2024-10-07 11:10:33 -04:00
Tim Edwards e659495ef5 Merge branch 'master' into netgen-1.5 2024-10-04 02:00:03 -04:00
Tim Edwards 5c21000a8b Made a modification to accommodate the situation where a SPICE
instance is matched to a verilog module definition, and the SPICE
instance is read before the verilog definition, forcing a
placeholder cell to be created.  Netgen will now make the
assumption that the verilog ports are in the same order as the
SPICE instance port order.  At the same time, it will output a
warning message that it is making this not-necessarily-warranted
assumption.  If the number of ports don't match or the placeholder
did not come from a SPICE instance, then the placeholder pins are
left alone.
2024-10-03 14:52:42 -04:00
Tim Edwards 2ce3cf8dd9 Merge branch 'master' into netgen-1.5 2024-10-03 02:00:02 -04:00
Tim Edwards 05872ca918 Corrected an apparently long-standing error that is responsible for
some errors failing to list in the output while also being responsible
for a number of non-errors showing up in the output.  This fix may
substantially clean up netgen output.  Also:  Added text to the
output noting that pin matching may be incorrect with respect to
symmetries if the nets have failed to match.
2024-10-02 21:20:27 -04:00
Tim Edwards 05f433f334 Merge branch 'master' into netgen-1.5 2024-10-01 02:00:02 -04:00
Tim Edwards e821381900 Corrected a rather obscure error in which an otherwise unconnected
port-to-port short (formed by "assign" in verilog or zero-valued
resistors in SPICE) does not get checked when counting nodes
before adding a proxy pin to a subcircuit in that cell, causing
the proxy pin to be assigned the same node number and forming an
unintended connection to the port-to-port connecting net.
2024-09-30 22:11:53 -04:00
Tim Edwards 2129073a38 Merge branch 'master' into netgen-1.5 2024-09-28 02:00:02 -04:00
Tim Edwards 8022e1370f Added a few lines to rebuild the node cache after removing devices
such a zero-ohm resistors or zero-volt sources during the pre-match
phase, since the list of nodes gets changed by merging nets across
the removed devices.  Otherwise, the node-name cache gets
corrupted and random LVS errors occur.
2024-09-27 10:08:37 -04:00
Tim Edwards b0d980bb7d Merge branch 'master' into netgen-1.5 2024-08-17 02:00:03 -04:00
Tim Edwards 2b88d79adc Corrected a rare case where a NULL value propagates in the flattening
routine and is not caught until it causes a segfault.
2024-08-16 19:48:36 -04:00
Tim Edwards ab0165b16c Merge branch 'master' into netgen-1.5 2024-05-17 02:00:02 -04:00
Tim Edwards bf4112db07 Corrected two statements that can cause a segfault because a
structure variable is not checked for the condition of being NULL
before attempting to read a component of the structure.  These
conditions imply that something is badly wrong in the netlist but
should not be causing a segfault.
2024-05-16 11:49:56 -04:00
Tim Edwards 5197eb6186 Merge branch 'master' into netgen-1.5 2024-05-15 02:00:01 -04:00
Tim Edwards fcee934580 Corrected the parsing of the "model" command, which was failing to
pass the right cell name to the routine which counts the number of
pins.  Using this in a setup file will prevent netgen from spending
time matching low-level devices.
2024-05-14 15:12:41 -04:00
Tim Edwards 48ed1f7583 Merge branch 'master' into netgen-1.5 2024-05-10 02:00:02 -04:00
Tim Edwards 2d427aef3c Corrected the bad placement of #ifdef TCL_NETGEN . . . #endif around
critical parts of the netcmp.c code, causing issues with the non-Tcl
build (not that anyone should be doing a non-Tcl build).
2024-05-09 14:11:26 -04:00
Tim Edwards e63593c7e2 Merge branch 'master' into netgen-1.5 2024-04-04 02:00:01 -04:00
Tim Edwards fd0c8c87ea Corrected another error in which, for device sorting, "M" was set
to 1 before the loop over devices in "run", resulting in "M"
taking the value of the previous property record if the following
record did not have an "M" value, instead of setting it to 1.
2024-04-03 21:05:08 -04:00
Tim Edwards 3d180f778d Corrected an error that had previously been corrected in
PropertyMatch() but not corrected symmetrically between circuit1
and circuit2;  this left the possibility that "M=1" in one
circuit vs. no "M" entry in the other would still pop up as a
property error, depending on which circuit (layout or schematic)
was listed first.
2024-04-03 11:01:58 -04:00
Tim Edwards 178af5f493 Merge branch 'master' into netgen-1.5 2024-03-05 02:00:02 -05:00
Tim Edwards 035fef5c72 Corrected an issue that prevents "cells list <file>" from reporting
empty cells (this does not solve the problem at hand, but is a part
of it).
2024-03-04 21:26:09 -05:00
Tim Edwards ac8956c32e Merge branch 'master' into netgen-1.5 2024-02-20 02:00:03 -05:00
Tim Edwards 202ea0431f Also updated configure (in addition to configure.in) with the
change to remove the "m4" dependency.
2024-02-19 12:46:20 -05:00
Tim Edwards 94754dbc4e Removed the requirement for package "m4" that is in the configuration
script.  It is not needed and doesn't exist in many OS distributions.
2024-02-19 12:41:08 -05:00
Tim Edwards b87f0fd5db Merge branch 'master' into netgen-1.5 2024-02-19 02:00:03 -05:00
Tim Edwards bf67d3c275 Having been given an example by Kareem Farid where the order of
verilog netlists makes a difference to the matching (or failure
thereof), I applied the same in-circuit pin matching as previously
applied to mixtures of SPICE and verilog netlists.  This is clearly
a more robust way to handle pin order differences between parent
and child than was implemented previously.
2024-02-18 15:22:40 -05:00
Tim Edwards 21e9207924 Merge branch 'master' into netgen-1.5 2024-02-10 02:00:02 -05:00
Tim Edwards 62feed812e Corrected an issue that arose due to a change made earlier: A
while back, shorted pins were moved into contiguous positions.
When that method was discovered to cause matching issues, it was
abandoned with a note that doing so might have unintended
consequences because other code might depend on the shorted pins
being contiguous.  Such a case was just found, and corrected.
However, it was also found that shorted pins were still not
completely handled correctly in MatchPins();  a solution was
found that adds such pins to the "permutes" list (which needs to
be done if the shorted pins are to be correctly handled in any
higher level of the hierarchy, if there is one), and the
"permutes" list is then checked by MatchPins() to determine if
pins match because they belong to the same group of shorted
pins.
2024-02-09 21:23:28 -05:00
Tim Edwards 8392721885 Merge branch 'master' into netgen-1.5 2024-02-07 02:00:02 -05:00
Tim Edwards d1c2848e4b Corrected another error in which some simple expressions are
incorrectly evaluated;  "(w+l)" for example treats "w+l" as
a single string instead of three tokens.  Corrected the code
to watch for a failure of strtod() when parsing the expression
at the "+" sign (also for "-").
2024-02-06 16:27:32 -05:00
Tim Edwards 6b0bd4d97b Found an error with the property sorting in which float values
were not compared for sorting in the same way they are compared
for property matching.  The "slop" value was treated as absolute,
not a percentage, so for example a slop of 0.01 on a dimension
of microns would cause all dimensions to be treated as round-off
error, and no sorting would occur.
2024-02-06 15:02:50 -05:00
Tim Edwards 79bab50a79 Merge branch 'master' into netgen-1.5 2024-02-04 02:00:02 -05:00
Tim Edwards d69fbc23bb Added code to handle the problem in which a verilog netlist is read
before its component cells, and the component cells are read in as
SPICE netlists.  Then the original verilog cell and its instances
need to have pins reordered to match the subcircuit definition in
the SPICE netlist.  Otherwise, when verilog and SPICE netlists are
mixed, the order in which the files are read is critical, and
failures due to reading out-of-order are very obscure and nearly
impossible to debug.
2024-02-03 21:21:09 -05:00
Tim Edwards b83800d69b Merge branch 'master' into netgen-1.5 2024-02-03 02:00:02 -05:00
Tim Edwards c7fa0324d9 Added a piece of code that handles implicit pins in verilog by doing
the following:  (1) Checking that the parent cell is verilog,
(2) only running after the two cells themselves have been compared
and matched, then (3) added the missing pin or pins while reordering
pins on instances (note: this may not work if the verilog netlist is
the first passed to netgen;  that case needs to be checked).
2024-02-02 14:51:10 -05:00
Tim Edwards 93b9cf6577 Merge branch 'master' into netgen-1.5 2024-01-04 02:00:02 -05:00
Tim Edwards eb27a18ae3 Corrected two different errors:
(1) When a comment line follows a ".subckt" line, and the comment
    line is empty or all whitespace, then the following line would
    be ignored.  This condition appears to be very specific and
    was solved simply by detecting it and handling it.
(2) Occasionally the "M" parameter of a subcircuit will be recorded
    as type double, and this was not being anticipated by the code
    that checks if "M=1" matches a corresponding entry with no "M"
    parameter.  Simple fix to check the condition where the "M"
    parameter is type double.
2024-01-03 21:21:03 -05:00
Tim Edwards bf53d52970 Merge branch 'master' into netgen-1.5 2023-12-04 02:00:02 -05:00
Tim Edwards 1817f4dd6a Corrected the LDDL_FLAGS setting for Mac OS, which is to replace
"-flat_namespace -undefined suppress -noprebind" to "-undefined
dynamic_lookup" which is what was done in magic, which has a
similar structure to netgen.
2023-12-03 20:32:05 -05:00
Tim Edwards 4250525e19 Merge branch 'master' into netgen-1.5 2023-11-21 02:00:01 -05:00
Tim Edwards a7e859fcde Corrected an error in parallel_sort and series_sort that does not
move to the start index before relinking the sorted entries.  That
will cause properties to be lost whenever the start index is greater
than zero.  Not sure why this hasn't been caught previously, or
whether other errors are involved here.
2023-11-20 10:38:41 -05:00
Tim Edwards c1ed4ce49e Merge branch 'master' into netgen-1.5 2023-10-28 02:00:04 -04:00
Tim Edwards 6d23844483 Corrected an error in the flattening routine that will cause the
"flatten" command to crash if there are black-box subcircuits in
the netlist.
2023-10-27 10:35:27 -04:00
Tim Edwards cc84364263 Added code to the netgen Tcl startup script to grab the PDK_ROOT
variable used with open_pdks, so that PDK references can be made
independent of the local filesystem.
2023-10-27 09:54:21 -04:00
Tim Edwards 00b906c109 Corrected an error in the previous commit which made primitive
devices from a .prm file into class "subckt" when they should be
class "module" (because they are primitives).  Otherwise, netgen
will crash when attempting to flatten them.
2023-10-27 09:38:14 -04:00
Tim Edwards ab327c5f82 Merge branch 'master' into netgen-1.5 2023-10-27 02:00:02 -04:00
Tim Edwards 25a0e12428 One correction to the last commit, to ensure that subcircuits
which are MOSFETs are output into the .sim file in correct
G-D-S-B order.
2023-10-26 20:50:43 -04:00
Tim Edwards eabb898578 Added support for converting a SPICE file to a SIM file simulatable
with IRSIM including recent changes made to support multiple device
types using the subcircuit "x" component type.  This requires
reading in a .prm file, which incidentally can be used with any
SPICE file to inform netgen of the specific component type of any
model defined as a subcircuit.
2023-10-26 15:08:20 -04:00
Tim Edwards 2cb3937ee3 Merge branch 'master' into netgen-1.5 2023-10-23 02:00:02 -04:00
Tim Edwards ec0e097fcf Corrected the code from version 258 which was supposed to handle
the removal of zero-valued devices between ports on the top level
(from version 254 they are ignored for levels under the top to
prevent port order from getting scrambled).  An invalid check was
being made to determine if the cells being compared were the top
of the compare queue.  This has been fixed.
2023-10-22 10:36:54 -04:00
Tim Edwards 9b0afa893d Merge branch 'master' into netgen-1.5 2023-10-04 02:00:01 -04:00
Tim Edwards f59c9ebcb7 Corrected an issue where a mismatch in property type (e.g.,
string vs. integer) will cause a segfault.  Not sure if
type promotion is needed at that point because the failing case
was a syntax error that caused a double value to be interpreted
as a string because it could not be cast into a numeric form.
2023-10-03 19:39:01 -04:00
Tim Edwards c0f7ebd625 Merge branch 'master' into netgen-1.5 2023-09-05 02:00:01 -04:00
Tim Edwards ce097d5d76 One minor change to the previous commit: The check for shorting
devices between two ports is ignored for top-level cells, because
the scrambled ports won't affect anything in that case, and the
error will be reported as a port error, as it should.
2023-09-04 14:47:23 -04:00
Tim Edwards 619409556c Modified the handling of zero-valued resistors and voltage sources
so that they are *not* removed to make a better match if they are
shorting across two ports.  If removed, then the port lists will
get screwed up.  It is better to let the subcircuits fail matching.
Then, after the mismatched subcircuits are flattened, if the zero-
valued resistor or voltage source no longer connects two ports, it
can be safely removed to make a better match.
2023-09-04 14:40:30 -04:00
Tim Edwards b1374e2bc8 Made two changes to the verilog token parsing in netfile.c in
response to Mitch Bailey's github issue #82:

(1) When skipping comments, skip the contents of "(* ... *)"
    delimiters as well as "/* ... */" delimiters.

(2) When checking for qflow's "\abcd\" names (final space
    replaced with a backslash for SPICE compatibility of
    names), make sure that the last "\" is followed by end-
    of-string.  Otherwise names like "\a\bcd " will fail to
    parse correctly.
2023-09-04 10:50:59 -04:00
Tim Edwards 9e9288f746 Merge branch 'master' into netgen-1.5 2023-09-02 02:00:02 -04:00
Tim Edwards cff954f36a Removed a block of ill-considered code that moves pins together
when they are shorted, because doing so is scrambling the pin
order of cells with respect to the instance calls to the cell.
Not sure if there is any code that relies on shorted pins being
adjacent, though.
2023-09-01 16:04:41 -04:00
Tim Edwards c27d933adc Modified some of the verilog read-in code to avoid a segmentation
fault that would happen if the verilog had illegal syntax of a
misspelled net name (although normally netgen is expected not to
have to check the verilog for syntax, and there are probably many
such cases of netgen failing to handle incorrect verilog and then
crashing as a result).
2023-09-01 09:04:44 -04:00
Tim Edwards bcbc736b51 Merge branch 'master' into netgen-1.5 2023-08-28 02:00:03 -04:00
Tim Edwards 615c55cbe2 Updated the config.guess and config.sub files; the newer ones
support, among other things, RISC-V.
2023-08-27 11:53:16 -04:00
Tim Edwards f5f1240073 Merge branch 'master' into netgen-1.5 2023-07-12 02:00:03 -04:00
Tim Edwards 87d8759a69 Corrected part of the MatchPins() routine so that the JSON output
tracks the output printed to stdout when matching pins.  One section
of this subroutine used the wrong pointers when writing to the Tcl
list (for eventual JSON output) which was the fundamental error.
Beyond that, the "debug" case (if used) would fail to run some of
the matching code, and the "no matching pin" case needed to be
handled for the Tcl list output.  Now the terminal output, terminal
debug output, and Tcl list output should all be in agreement on the
pin lists.
2023-07-11 15:40:00 -04:00
Tim Edwards 02d7a1bd01 Merge branch 'master' into netgen-1.5 2023-06-13 02:00:02 -04:00
Tim Edwards 1efa054ac1 Corrected an issue with shorted ports. When shorted ports are
connected only to ports and not to any devices, then they do not
show up in NodeClasses() and so pass through most of the checks
in MatchPins().  A separate correspondence check is needed to make
sure that the same shorted ports appear in both netlists.
2023-06-12 17:16:49 -04:00
Tim Edwards 7878b3cfbc Merge branch 'master' into netgen-1.5 2023-04-15 02:00:01 -04:00
Tim Edwards 609d1de250 Corrected a problem in the flattening routine, which was a missing
method for flattening a subcircuit with property M != 1.
2023-04-14 20:09:36 -04:00
Tim Edwards 7f727e6674 Merge branch 'master' into netgen-1.5 2023-04-14 02:00:01 -04:00
Tim Edwards 47c3b34612 Corrected the node merging around zero-volt voltage sources and
zero-ohm resistors so that if one of the nodes being merged is a
port, it is preferred over the other.
2023-04-13 15:41:12 -04:00
Tim Edwards d111fa0a3b Merge branch 'master' into netgen-1.5 2023-03-30 02:00:02 -04:00
Tim Edwards 66317c9848 Corrected an error in order-of-precedence of arithmetic operators,
ensuring that, e.g., in (a)*b+c, (a)*b gets evaluated before b+c.
2023-03-29 19:45:39 -04:00
Tim Edwards eeb3c0e5c6 Added support for simple forms of for() loops in generate blocks.
This is done by treating the loop variable as a temporary parameter
that is valid only inside the loop, and changing the parameter
value on each loop iteration.  The file stream position is used
to iterate the loop with calls to fseek() and ftell(), so that the
input tokenizer continues to work within loops.
2023-03-29 16:17:37 -04:00
Tim Edwards 490f9f7dbc Added a missing check for using a verilog macro definition as an
array delimeter for an instance array in verilog.
2023-03-29 09:54:45 -04:00
Tim Edwards 178b172c06 Merge branch 'master' into netgen-1.5 2023-03-08 02:00:03 -05:00
Tim Edwards 40cf82c2cb Slightly modified the fix from the last commit to allow an empty
string for the setup file to be the "trivial default" previously
used in case of the setup file not being found.  Put a newline
around the setup file messages so that they stand out from the
rest of the initial output information.
2023-03-07 09:00:39 -05:00
Tim Edwards 1ac2b592fb Changed what was a not-very-well thought out behavior: On being
passed an invalid setup file, the netgen "lvs" script uses a
trivial default setup and issues no error or warning.  Replaced
this behavior with an error message and a hard stop.
2023-03-07 08:53:06 -05:00
Tim Edwards 7870538ec9 Merge branch 'master' into netgen-1.5 2023-03-07 02:00:02 -05:00
Tim Edwards e12883037c Modified code from EquivalenceClasses() that forces the two cells
to have unique class hashes.  This has the problem that it prevents
comparing N-to-1 cells because declaring X->X1 as equivalent breaks
the original name equivalence of X->X.  The new implementation adds
the switch "-unique" to preserve the original behavior.  Otherwise,
the class hashes are made the same as the 2nd cell passed to the
command, and it is the responsibility of the person running LVS to
ensure that this is done in the correct direction.
2023-03-06 09:36:35 -05:00
Tim Edwards ee4e1e087f Merge branch 'master' into netgen-1.5 2023-03-05 02:00:01 -05:00
Tim Edwards 2d63fd63c1 Corrected the wrong order of arguments in an strncpy() command
that was introduced in a recent pull request, as pointed out by
Mitch Bailey in github issue #72.
2023-03-04 10:05:57 -05:00
Tim Edwards 7a8b5e835b Merge branch 'master' into netgen-1.5 2023-03-01 02:00:03 -05:00
Tim Edwards e557e61a02 Updated version and fixed a redundant include statement after
merging pull request #71 from Donn.
2023-02-28 09:04:45 -05:00
Donn 67da250615 Patches for Clang 2023-02-28 09:02:41 -05:00
Tim Edwards 77f7a773e3 Merge branch 'master' into netgen-1.5 2023-02-28 02:00:03 -05:00
Tim Edwards cd013621a7 Relaxed the prohibition on ((S != 1) && (M != 1)) in device
network parallel/series networks.  Instead, added a global option
with command "property tolerance strict|relaxed" to reinstate the
original (strict) behavior on demand, while relaxing it by default.
This allows certain series/parallel networks to match numerically
even though the schematic netlist may have combined individual
devices.
2023-02-27 15:26:18 -05:00
Tim Edwards 9a48a59f68 Merge branch 'master' into netgen-1.5 2023-02-25 02:00:03 -05:00
Tim Edwards 45712a04f1 Removed X11 definitions from the configuration when compiling
with Tcl, since Tk is launched independently through the console
script and nothing inside of netgen itself involves graphics.
This prevents netgenexec from linking to Tk and X11 libraries.
2023-02-24 10:42:59 -05:00
Tim Edwards 535b8285e9 Merge branch 'master' into netgen-1.5 2022-12-20 02:00:03 -05:00
Tim Edwards 28a2950439 Modified netgen output to not print information about combining
individual components in parallel and series as it reduces the
networks.  This information is available in total in the cell
summary.
2022-12-19 14:37:11 -05:00
Tim Edwards 1e1d506697 Merge branch 'master' into netgen-1.5 2022-12-16 02:00:03 -05:00
Tim Edwards 013fff9f37 (1) Fixed the series sorting, which needed to be modified to match
the parallel sorting routine.  This fixes occasional property
errors with series-connected devices such as resistors.  (2) Added
a method to associate properties with specific pins when pins are
permutable.  This allows netgen to properly check a value like
source/drain area when the definition of source and drain has
changed due to permutation of the device.  (3) Added a "property"
command extension "associate" to associate a property with a pin,
for use with the method described in (2).
2022-12-15 21:34:56 -05:00
Tim Edwards 4edaf0813d Merge branch 'master' into netgen-1.5 2022-11-17 02:00:01 -05:00
Tim Edwards 2292ab813b Corrected a badly implemented routine that can cause very long
run-times on large projects where a lot of cells need to be
deleted.
2022-11-16 12:37:05 -05:00
Tim Edwards a4ae5ed989 Merge branch 'master' into netgen-1.5 2022-11-05 02:00:01 -04:00
Tim Edwards 7e8508db53 Additional correction to the property match subroutine to better
check instances with permutable pins when checking parallelized
instances with disconnected pins vs. mutually connected pins.
2022-11-04 20:40:37 -04:00
Tim Edwards c9f7b24e0f Found an error in property matching causing weird errors in the
LVS result.  The property matching was failing to match (M=1) to
(M!=1) if M was not registered as a property name (which it often
isn't).  This would allow devices with different numbers of
instances in parallel to be put in the same matching group,
which then could later identify as a mismatch if the instances
were checked in a different order.
2022-11-04 12:07:21 -04:00
Tim Edwards d850586a14 Merge branch 'master' into netgen-1.5 2022-11-03 02:00:03 -04:00
Tim Edwards 95605ebbd4 Prevented checks from automatically treating two empty cells as
black boxes.  The check was supposed to check that both empty
cells really are black box entries.
2022-11-02 09:48:39 -04:00
27 changed files with 6391 additions and 2652 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.239
1.5.301
+298 -7
View File
@@ -31,6 +31,8 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "print.h"
#include "hash.h"
static int invlambda = 100; /* Used in sim and prm files */
void extCell(char *name, int filenum)
{
struct nlist *tp, *tp2;
@@ -450,7 +452,8 @@ void simCell(char *name, int filenum)
struct nlist *tp, *tp2;
struct objlist *ob, *ob2;
char FileName[500], simclass;
short i;
char writeLine[1024], paramString[128];
short i, p, mult;
double l, w, v;
tp = LookupCellFile(name, filenum);
@@ -478,9 +481,9 @@ void simCell(char *name, int filenum)
}
/* print out header list */
/* distance units are multiplied by 100 (distances are in um) */
/* distance units are multiplied by invlambda */
FlushString("| units: 100 tech: scmos\n");
FlushString("| units: %d tech: scmos\n", 100 * invlambda);
/* now run through cell's contents, print instances */
for (ob = tp->cell; ob != NULL; ob = ob->next) {
@@ -516,13 +519,17 @@ void simCell(char *name, int filenum)
case CLASS_NPN:
simclass = 'b';
break;
default:
case CLASS_SUBCKT:
case CLASS_MODULE:
simclass = 'x';
break;
default:
simclass = '|';
break;
}
if (simclass != 'x')
FlushString("%c", simclass);
FlushString("%c", simclass);
switch (tp2->class) {
case CLASS_NMOS: case CLASS_NMOS4:
@@ -590,6 +597,115 @@ void simCell(char *name, int filenum)
FlushString(" %g\n", v);
break;
case CLASS_MODULE:
*writeLine = 'x';
*(writeLine + 1) = '\0';
mult = 1;
/* Important---Need to look up the cell definition; if
* the first pin is "drain" then it is a FET, and pins
* get swapped from D-G-S-B to G-S-D-B. Source and drain
* are treated here as equivalent because the .sim format
* has no concept of an asymmetric source and drain.
*/
ob2 = tp2->cell;
if ((*matchfunc)(ob2->next->name, "gate"))
{
strcat(writeLine, " ");
strcat(writeLine, NodeAlias(tp, ob->next));
strcat(writeLine, " ");
strcat(writeLine, NodeAlias(tp, ob));
ob2 = ob->next->next;
}
else
ob2 = ob;
while (ob2 != NULL) {
strcat(writeLine, " ");
strcat(writeLine, NodeAlias(tp, ob2));
ob2 = ob2->next;
if ((ob2 == NULL) || (ob2->type <= FIRSTPIN)) break;
}
if (ob2 && ob2->type == PROPERTY) {
struct valuelist *vl;
/* Only known parameters are L, W, X, and Y */
for (p = 0;; p++) {
vl = (struct valuelist *)(&(ob2->instance.props[p]));
if (vl->type == PROP_ENDLIST) {
strcat(writeLine, " l=1");
break;
}
else if ((*matchfunc)(vl->key, "L")) {
v = vl->value.dval;
sprintf(paramString, " l=%d", (int)(0.5 + (v * invlambda)));
strcat(writeLine, paramString);
break;
}
}
for (p = 0;; p++) {
vl = (struct valuelist *)(&(ob2->instance.props[p]));
if (vl->type == PROP_ENDLIST) {
strcat(writeLine, " w=1");
break;
}
else if ((*matchfunc)(vl->key, "W")) {
v = vl->value.dval;
sprintf(paramString, " w=%d", (int)(0.5 + (v * invlambda)));
strcat(writeLine, paramString);
break;
}
}
for (p = 0;; p++) {
vl = (struct valuelist *)(&(ob2->instance.props[p]));
if (vl->type == PROP_ENDLIST) {
strcat(writeLine, " x=0");
break;
}
else if ((*matchfunc)(vl->key, "X")) {
i = vl->value.ival;
sprintf(paramString, " x=%d", i);
strcat(writeLine, paramString);
break;
}
}
for (p = 0;; p++) {
vl = (struct valuelist *)(&(ob2->instance.props[p]));
if (vl->type == PROP_ENDLIST) {
strcat(writeLine, " y=0");
break;
}
else if ((*matchfunc)(vl->key, "Y")) {
i = vl->value.ival;
sprintf(paramString, " y=%d", i);
strcat(writeLine, paramString);
}
}
for (p = 0;; p++) {
vl = (struct valuelist *)(&(ob2->instance.props[p]));
if (vl->type == PROP_ENDLIST) {
break;
}
else if ((*matchfunc)(vl->key, "M")) {
if (vl->type == PROP_INTEGER)
mult = vl->value.ival;
else
mult = vl->value.dval;
}
}
}
strcat(writeLine, " ");
strcat(writeLine, tp2->name);
strcat(writeLine, "\n");
/* Multiple instances (M != 1) are written multiple times.
* NF is ignored in favor of having a single device with
* the total width.
*/
for (i = 0; i < mult; i++)
FlushString(writeLine);
break;
default:
FlushString("| unhandled component %s\n", tp2->name);
break;
@@ -626,6 +742,181 @@ int StrIsInt(char *s)
return (1);
}
/*------------------------------------------------------*/
/* Read a .prm format file. This is specifically to */
/* get the "device" lines that indicate how a SPICE */
/* subcircuit model or a .sim "x" record needs to be */
/* translated into a specific component type like a */
/* FET, diode, resistor, etc. */
/*------------------------------------------------------*/
char *ReadPrm(char *fname, int *fnum)
{
int filenum;
struct keyvalue *kvlist = NULL;
struct nlist *tp;
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
char name[MAX_STR_LEN];
SetExtension(name, fname, PRM_EXTENSION);
if (OpenParseFile(name, *fnum) < 0) {
Printf("Error in prm file read: No file %s\n",name);
*fnum = filenum;
return NULL;
}
}
/* Make sure all .prm file reading is case INsensitive */
/* This is because the only reason to read a PRM file */
/* is to find all the subcircuit device types so that */
/* a SPICE file can be read and a SIM file written. */
matchfunc = matchnocase;
matchintfunc = matchfile;
hashfunc = hashnocase;
CellDef(fname, filenum);
while (!EndParseFile()) {
char devicename[MAX_STR_LEN];
SkipTok(NULL);
if (EndParseFile()) break;
if (nexttok[0] == ';') continue; /* Comment line */
else if (nexttok[0] == '\0') continue; /* Blank line */
else if (match(nexttok, "lambda")) {
SkipTok(NULL);
invlambda = (int)(0.5 + (1.0 / atof(nexttok)));
SkipNewLine(NULL); /* skip any attributes */
}
else if (match(nexttok, "device")) {
SkipTok(NULL);
if (match(nexttok, "nfet")) {
SkipTok(NULL);
strcpy(devicename, nexttok);
/* Create 4-terminal nfet subcircuit device record */
if (LookupCellFile(devicename, filenum) == NULL) {
CellDef(devicename, filenum);
Port("drain");
Port("gate");
Port("source");
Port("bulk");
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
PropertyInteger(devicename, filenum, "nf", 0, 1);
PropertyInteger(devicename, filenum, "m", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef(fname, filenum);
}
LinkProperties(devicename, kvlist);
SkipNewLine(NULL); /* skip any attributes */
}
else if (match(nexttok, "pfet")) {
SkipTok(NULL);
strcpy(devicename, nexttok);
/* Create 4-terminal pfet subcircuit device record */
if (LookupCellFile(devicename, filenum) == NULL) {
CellDef(devicename, filenum);
Port("drain");
Port("gate");
Port("source");
Port("well");
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
PropertyInteger(devicename, filenum, "nf", 0, 1);
PropertyInteger(devicename, filenum, "m", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef(fname, filenum);
}
LinkProperties(devicename, kvlist);
SkipNewLine(NULL); /* skip various attributes */
}
else if (match(nexttok, "resistor")) {
SkipTok(NULL);
strcpy(devicename, nexttok);
/* Resistor device has additional record for the value */
/* (Need to do something with this. . . ?) */
SkipTok(NULL);
/* Create resistor subcircuit device record */
if (LookupCellFile(devicename, filenum) == NULL) {
CellDef(devicename, filenum);
Port("end_a");
Port("end_b");
PropertyDouble(devicename, filenum, "value", 0.01, 0.0);
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
PropertyInteger(devicename, filenum, "m", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef(fname, filenum);
}
LinkProperties(devicename, kvlist);
SkipNewLine(NULL); /* skip various attributes */
}
else if (match(nexttok, "capacitor")) {
SkipTok(NULL);
strcpy(devicename, nexttok);
/* Capacitor device has additional record for the value */
/* (Need to do something with this. . . ?) */
SkipTok(NULL);
/* Create capacitor subcircuit device record */
if (LookupCellFile(devicename, filenum) == NULL) {
CellDef(devicename, filenum);
Port("top");
Port("bottom");
PropertyDouble(devicename, filenum, "value", 0.01, 0.0);
PropertyDouble(devicename, filenum, "l", 0.01, 0.0);
PropertyDouble(devicename, filenum, "w", 0.01, 0.0);
PropertyInteger(devicename, filenum, "m", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef(fname, filenum); /* Reopen */
}
LinkProperties(devicename, kvlist);
SkipNewLine(NULL);
}
else if (match(nexttok, "diode")) {
SkipTok(NULL);
strcpy(devicename, nexttok);
/* Create diode subcircuit device record */
if (LookupCellFile(devicename, filenum) == NULL) {
CellDef(devicename, filenum);
Port("anode");
Port("cathode");
PropertyInteger(devicename, filenum, "m", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef(fname, filenum); /* Reopen */
}
LinkProperties(devicename, kvlist);
SkipNewLine(NULL);
}
else {
Printf("Unknown device type in .prm: '%s'\n", nexttok);
InputParseError(stderr);
SkipNewLine(NULL);
}
}
else {
/* Could spell out all the keywords used in .prm files */
/* but probably not worth the effort. */
SkipNewLine(NULL);
}
DeleteProperties(&kvlist);
}
EndCell();
CloseParseFile();
tp = LookupCellFile(fname, filenum);
if (tp) tp->flags |= CELL_TOP;
*fnum = filenum;
return fname;
}
/*-------------------------*/
/* Read a .sim format file */
/*-------------------------*/
@@ -644,7 +935,7 @@ char *ReadSim(char *fname, int *fnum)
SetExtension(name, fname, SIM_EXTENSION);
if (OpenParseFile(name, *fnum) < 0) {
Printf("Error in ext file read: No file %s\n",name);
Printf("Error in sim file read: No file %s\n",name);
*fnum = filenum;
return NULL;
}
@@ -847,7 +1138,7 @@ char *ReadSim(char *fname, int *fnum)
}
else if (match(nexttok, "r")) { /* 2-port resistors */
if (IgnoreRC) {
/* ignore all capacitances */
/* ignore all resistances */
SkipNewLine(NULL);
}
else {
+194 -34
View File
@@ -136,17 +136,16 @@ void flattenCell(char *name, int file)
ob2 = ob2->next;
}
/* delete all port elements from child */
while (IsPort(ChildObjList)) {
/* delete all ports at beginning of list */
if (Debug) Printf("deleting leading port from child\n");
tmp = ChildObjList->next;
FreeObjectAndHash(ChildObjList, ChildCell);
ChildObjList = tmp;
if ((ChildObjList = tmp) == NULL) break;
}
tmp = ChildObjList;
while (tmp->next != NULL) {
while (tmp && (tmp->next != NULL)) {
if (IsPort(tmp->next)) {
ob2 = (tmp->next)->next;
if (Debug) Printf("deleting a port from child\n");
@@ -536,11 +535,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
/* Do property inheritance */
/* NOTE: Need to do: Check properties for M > 1 and decrement
* and repeat without moving CurrentProp
*/
if (CurrentProp) {
int i, mval;
struct valuelist *kv;
for (ob2 = ChildStart; ob2 != NULL; ob2=ob2->next) {
/* If the parent cell has properties to declare, then */
@@ -548,10 +546,49 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
/* spiceparams dictionary is active (during file */
/* reading only). */
if (ob2->type == PROPERTY)
if (ob2->type == PROPERTY) {
ReduceExpressions(ob2, CurrentProp, ChildCell,
(spiceparams.hashtab == NULL) ? 0 : 1);
}
}
}
/* Check for property M. If it exists and is greater than */
/* one, reduce it and repeat; i.e., generate multiple */
/* child instances to match the M value. Probably this */
/* could be done quicker by just creating a new property */
/* M for a single child. */
mval = 0;
for (i = 0; ; i++) {
kv = &(CurrentProp->instance.props[i]);
if (kv->type == PROP_ENDLIST) break;
if ((*matchfunc)(kv->key, "M")) {
if (kv->type == PROP_INTEGER) {
mval = kv->value.ival;
kv->value.ival = mval - 1;
break;
}
else if (kv->type == PROP_DOUBLE) {
mval = (int)kv->value.dval;
kv->value.dval = (double)mval - 1;
break;
}
}
}
if (mval > 1) {
/* Put the child cell at the start of ChildObjList */
if (ChildEnd) {
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
/* Continue without moving CurrentProp */
/* Note that if ChildEnd is NULL then the child cell
* is optimized out and there is no need to do it
* M times.
*/
continue;
}
}
/* Repeat for each property record, as each property represents a
* unique instance that must be flattened individually.
@@ -563,8 +600,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
else break;
/* Put the child cell at the start of ChildObjList */
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
if (ChildEnd) {
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
}
}
/* Put the child cell at the start of ChildObjList */
@@ -1177,7 +1216,6 @@ int UniquePins(char *name, int filenum)
firstport = (struct objlist **)CALLOC(maxnode + 1, sizeof(struct objlist *));
portcount = FIRSTPIN;
lob = NULL;
for (ob = ThisCell->cell; ob != NULL; ob = ob->next) {
if (ob->type != PORT) break;
if (ob->node > 0) {
@@ -1188,16 +1226,16 @@ int UniquePins(char *name, int filenum)
firstport[ob->node]->name, ThisCell->name, ThisCell->file);
/* Do not count this as a duplicate pin. */
nodecount[ob->node]--;
/* Move the pin adjacent to the one it is shorted to (if it
* isn't already); this will make the work of MatchPins() easier.
/* Note: Previously there was code here to move the shorted port
* next to the pin it is shorted to. This causes the cell def pins
* to become scrambled with respect to the pin order of its instances.
* Removed the code 9/1/2023. But---Not sure if any code depends
* on shorted pins being adjacent.
*/
if (firstport[ob->node]->next != ob) {
lob->next = ob->next;
ob->next = firstport[ob->node]->next;
firstport[ob->node]->next = ob;
ob = lob;
}
lob = ob;
/* When two pins are shorted, they are by definition permutable */
PermuteSetup(ThisCell->name, ThisCell->file, ob->name,
firstport[ob->node]->name);
continue;
}
else {
@@ -1218,7 +1256,6 @@ int UniquePins(char *name, int filenum)
}
}
portcount++;
lob = ob;
}
if (needscleanup)
@@ -1269,6 +1306,14 @@ int UniquePins(char *name, int filenum)
return 1;
}
/* Structure used below for keeping track of node numbers
* belonging to removed nodes.
*/
struct LinkedNum {
int node;
struct LinkedNum *next;
};
/*------------------------------------------------------*/
/* Callback function for CleanupPins */
/* Note that if the first pin of the instance is a */
@@ -1281,6 +1326,7 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
struct nlist *ptr;
struct objlist *ob, *obt, *lob, *nob, *firstpin, *pob;
struct nlist *tc = (struct nlist *)clientdata;
struct LinkedNum *newnodenum, *removedNodes = (struct LinkedNum *)NULL;
int pinnum;
char *saveinst = NULL;
@@ -1336,6 +1382,15 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
saveinst = ob->instance.name;
}
if (ob->model.class != NULL) FREE(ob->model.class);
// Record the net number of the pin being removed, to
// check at the end if the net belonged to a pin that
// got orphaned.
newnodenum = (struct LinkedNum *)MALLOC(sizeof(struct LinkedNum));
newnodenum->node = ob->node;
newnodenum->next = removedNodes;
removedNodes = newnodenum;
FREE(ob);
}
else {
@@ -1379,6 +1434,28 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
}
}
while (removedNodes != NULL) {
int nodenum = removedNodes->node;
struct objlist *ob2;
/* Only concerned with nodes that are in the pin list of ptr->cell */
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
if (ob->type != PORT) break;
if (ob->node == nodenum) break;
}
if (ob && (ob->type == PORT)) {
/* Check if this node number exists only in the port record */
for (nob = ob->next; nob != NULL; nob = nob->next)
if (nob->node == nodenum) break;
if (nob == NULL) {
ob->node = -1; /* This pin is now disconnected */
}
}
newnodenum = removedNodes;
removedNodes = removedNodes->next;
FREE(newnodenum);
}
if (saveinst != NULL) FREE(saveinst);
return NULL; /* Keep the search going */
}
@@ -1599,7 +1676,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
ECompare *ecomp, *ncomp;
ECompList *list0X, *listX0;
int hascontents1, hascontents2;
int match, modified = 0;
int match, modified1 = 0, modified2 = 0;
int not_top;
if (file1 == -1)
tc1 = LookupCell(name1);
@@ -1676,7 +1754,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
" makes a better match\n", ecomp->cell1->name,
name1, file1);
flattenInstancesOf(name1, file1, ecomp->cell1->name);
modified++;
modified1++;
}
if (ecomp->cell2 && (ecomp->num2 > 0) &&
(!(ecomp->cell2->flags & CELL_PLACEHOLDER))) {
@@ -1684,7 +1762,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
" makes a better match\n", ecomp->cell2->name,
name2, file2);
flattenInstancesOf(name2, file2, ecomp->cell2->name);
modified++;
modified2++;
}
}
@@ -1774,7 +1852,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
" makes a better match\n", ecomp->cell2->name,
name2, file2);
flattenInstancesOf(name2, file2, ecomp->cell2->name);
modified++;
modified2++;
}
}
}
@@ -1840,7 +1918,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
" makes a better match\n", ecomp->cell1->name,
name1, file1);
flattenInstancesOf(name1, file1, ecomp->cell1->name);
modified++;
modified1++;
}
}
}
@@ -1870,6 +1948,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
// Remove non-matching zero-value devices. This can
// be done on a per-instance basis.
not_top = (PeekCompareQueueTop(NULL, NULL, NULL, NULL) == -1) ? FALSE : TRUE;
ecomp = (ECompare *)HashFirst(&compdict);
while (ecomp != NULL) {
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
@@ -1916,6 +1996,33 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
break;
}
}
/* Do NOT remove shorting devices that */
/* connect two ports. Otherwise the */
/* port lists get screwed up. It is */
/* better in that case to force the */
/* cells to be declared mismatched. */
/* This is ignored for a top-level cell */
/* because it will just show up as a */
/* port mismatch error as it should. */
if ((not_top == TRUE) &&
(ecomp->cell1->class != CLASS_ISOURCE)) {
int found1 = FALSE;
int found2 = FALSE;
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
if (!IsPort(ob2)) break;
else if (ob2->node == node1)
found1 = TRUE;
else if (ob2->node == node2)
found2 = TRUE;
if (found1 && found2) {
found = FALSE;
break;
}
}
}
if (found) break;
}
if (found) {
@@ -1929,6 +2036,16 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
if (ecomp->cell1->class != CLASS_ISOURCE) {
/* merge node of endpoints */
/* Prefer a port node over a non-port node */
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
if (!IsPort(ob2)) break;
else if (ob2->node == node1) break;
else if (ob2->node == node2) {
int ntemp = node1;
node1 = node2;
node2 = ntemp;
}
}
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
if (ob2->node == node2)
ob2->node = node1;
@@ -1955,7 +2072,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
/* Remove from list */
ecomp->num1--;
modified++;
modified1++;
ob1 = lob;
}
@@ -2027,6 +2144,27 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
break;
}
}
/* (See comments above about removing shorts */
/* between two ports.) */
if ((not_top == TRUE) &&
(ecomp->cell2->class != CLASS_ISOURCE)) {
int found1 = FALSE;
int found2 = FALSE;
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
if (!IsPort(ob1)) break;
else if (ob1->node == node1)
found1 = TRUE;
else if (ob1->node == node2)
found2 = TRUE;
if (found1 && found2) {
found = FALSE;
break;
}
}
}
if (found) break;
}
if (found) {
@@ -2037,6 +2175,16 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
/* merge node of endpoints */
if (ecomp->cell2->class != CLASS_ISOURCE) {
/* Prefer a port node over a non-port node */
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
if (!IsPort(ob1)) break;
else if (ob1->node == node1) break;
else if (ob1->node == node2) {
int ntemp = node1;
node1 = node2;
node2 = ntemp;
}
}
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
if (ob1->node == node2)
ob1->node = node1;
@@ -2062,8 +2210,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
}
/* Remove from list */
ecomp->num1--;
modified++;
ecomp->num2--;
modified2++;
ob2 = lob;
}
@@ -2097,7 +2245,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
// are no other modifications, as this rule is relaxed compared to other
// rules, and the other rules should be exhaustively applied first.
if ((listX0 != NULL) && (list0X != NULL) && (modified == 0)) {
if ((listX0 != NULL) && (list0X != NULL) && ((modified1 + modified2) == 0)) {
ECompare *ecomp0X, *ecompX0;
ECompList *elist0X, *elistX0;
for (elistX0 = listX0; elistX0; elistX0 = elistX0->next) {
@@ -2129,7 +2277,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
flattenInstancesOf(name1, file1, ecompX0->cell1->name);
ecompX0->num1 = 0;
ecomp0X->num1 += ecompX0->num1;
modified++;
modified1++;
break;
}
}
@@ -2155,7 +2303,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
flattenInstancesOf(name2, file2, ecomp0X->cell2->name);
ecomp0X->num2 = 0;
ecompX0->num2 += ecomp0X->num2;
modified++;
modified2++;
break;
}
}
@@ -2186,5 +2334,17 @@ done:
FREE(list0X);
list0X = nextptr;
}
return modified;
// If either netlist was modified, rebuild its node cache
if (modified1 > 0) {
FreeNodeNames(tc1);
CacheNodeNames(tc1);
}
if (modified2 > 0) {
FreeNodeNames(tc2);
CacheNodeNames(tc2);
}
return modified1 + modified2;
}
+1
View File
@@ -17,6 +17,7 @@ along with this program; see the file copying. If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* inetcomp.c -- a simple wrapper to the NETCOMP() function */
#include "config.h"
#include <stdio.h>
#include "netgen.h"
+1089 -398
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -7,6 +7,7 @@ extern struct nlist *Circuit1;
extern struct nlist *Circuit2;
extern int ExhaustiveSubdivision;
extern int ExactTopology;
extern int left_col_end;
extern int right_col_end;
@@ -37,7 +38,8 @@ extern int PermuteSetup(char *model, int filenum, char *pin1, char *pin2);
extern int PermuteForget(char *model, int filenum, char *pin1, char *pin2);
extern int EquivalenceElements(char *name1, int file1, char *name2, int file2);
extern int EquivalenceNodes(char *name1, int file1, char *name2, int file2);
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2);
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2,
int dounique);
extern int IgnoreClass(char *name, int file, unsigned char type);
extern int MatchPins(struct nlist *tp1, struct nlist *tp2, int dolist);
extern int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run,
@@ -60,7 +62,7 @@ extern void RegroupDataStructures();
extern void FormatIllegalElementClasses();
extern void FormatIllegalNodeClasses();
extern int ResolveAutomorphsByProperty();
extern int ResolveAutomorphsByPin();
extern int ResolveAutomorphsByPin(int match_nets);
extern void SummarizeElementClasses(struct ElementClass *EC);
extern int remove_group_tags(struct objlist *ob);
@@ -68,6 +70,8 @@ extern int remove_group_tags(struct objlist *ob);
#ifdef TCL_NETGEN
extern int EquivalentNode();
extern int EquivalentElement();
extern void DeriveAreaProperty();
extern void DerivePerimeterProperty();
extern void enable_interrupt();
extern void disable_interrupt();
+68 -17
View File
@@ -183,7 +183,6 @@ void CloseFile(char *filename)
fclose(outfile);
}
/* STUFF TO READ INPUT FILES */
static char *line = NULL; /* actual line read in */
@@ -207,6 +206,21 @@ struct hashdict *definitions = (struct hashdict *)NULL;
#define WHITESPACE_DELIMITER " \t\n\r"
/*----------------------------------------------------------------------*/
/* Seek and Tell on infile stream, for use with handling generate */
/* loops in verilog. */
/*----------------------------------------------------------------------*/
void SeekFile(long offset)
{
fseek(infile, offset, SEEK_SET);
}
long TellFile()
{
return ftell(infile);
}
/*----------------------------------------------------------------------*/
/* TrimQuoted() --- */
/* Remove spaces from inside single- or double-quoted strings. */
@@ -315,6 +329,11 @@ int GetNextLineNoNewline(char *delimiter)
llen = strlen(line);
}
while (llen == linesize - 1) {
/* Note that in the rare case where a newline is in the last buffer
* position, we're done.
*/
if (*(line + llen - 1) == '\n') break;
newbuf = (char *)MALLOC(linesize + 501);
strcpy(newbuf, line);
FREE(line);
@@ -573,19 +592,31 @@ void SkipTokNoNewline(char *delimiter)
/* */
/* Modified 3/30/2015 to include the condition where a comment line is */
/* in the middle of a series of continuation lines. */
/* */
/* Modified 1/3/2024 to avoid skipping two lines if a line has only the */
/* comment character '*' followed by a newline. It seems that '\n' is */
/* being ignored in WHITESPACE_DELIMITER, but it's easier to write the */
/* code to find the exception rather than track down the problem in */
/* GetNextLine(). */
/*----------------------------------------------------------------------*/
void SpiceTokNoNewline(void)
{
int contline;
if ((nexttok = strdtok(NULL, WHITESPACE_DELIMITER, NULL)) != NULL) return;
if ((nexttok = strdtok0(NULL, WHITESPACE_DELIMITER, NULL, FALSE)) != NULL) return;
while (nexttok == NULL) {
contline = getc(infile);
if (contline == '*') {
GetNextLine(WHITESPACE_DELIMITER);
SkipNewLine(NULL);
char testline = ' ';
while ((testline == ' ') || (testline == '\t'))
testline = getc(infile);
if (testline != '\n') {
ungetc(testline, infile);
GetNextLine(WHITESPACE_DELIMITER);
SkipNewLine(NULL);
}
continue;
}
else if (contline != '+') {
@@ -597,7 +628,8 @@ void SpiceTokNoNewline(void)
}
/*----------------------------------------------------------------------*/
/* Skip to the next token, ignoring any C-style comments. */
/* Skip to the next token, ignoring any C-style comments and verilog */
/* "(* ... *)"-style comments. */
/*----------------------------------------------------------------------*/
void SkipTokComments(char *delimiter)
@@ -613,6 +645,11 @@ void SkipTokComments(char *delimiter)
SkipTok(delimiter);
if (nexttok) SkipTok(delimiter);
}
else if (match(nexttok, "(*")) {
while (nexttok && !match(nexttok, "*)"))
SkipTok(delimiter);
if (nexttok) SkipTok(delimiter);
}
else break;
}
}
@@ -669,7 +706,7 @@ void SpiceSkipNewLine(void)
/* the boundary between two-character and one-character delimiters. */
/*----------------------------------------------------------------------*/
char *strdtok(char *pstring, char *delim1, char *delim2)
char *strdtok0(char *pstring, char *delim1, char *delim2, char isverilog)
{
static char *stoken = NULL;
static char *sstring = NULL;
@@ -714,10 +751,10 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
/* should know whether it is parsing SPICE or verilog and handle the syntax */
/* accordingly (needs to be done). */
if (*s == '\\') {
if (isverilog && (*s == '\\')) {
s++;
while (*s != '\0') {
if ((*s == ' ') || (*s == '\\')) {
if ((*s == ' ') || ((*s == '\\') && (*(s + 1) == '\0'))) {
s++;
break;
}
@@ -785,6 +822,17 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
return sstring;
}
/*----------------------------------------------------------------------*/
/* strdtok() is the original string tokenizer. It calls strdtok0() */
/* with isverilog=TRUE, so that tokens are parsed as (potentially) */
/* verilog names, which includes verilog backslash notation. */
/*----------------------------------------------------------------------*/
char *strdtok(char *pstring, char *delim1, char *delim2)
{
return strdtok0(pstring, delim1, delim2, TRUE);
}
/*----------------------------------------------------------------------*/
void InputParseError(FILE *f)
@@ -866,7 +914,7 @@ char *ReadNetlist(char *fname, int *fnum)
};
#ifdef mips
struct filetype formats[7];
struct filetype formats[8];
formats[0].extension = NTK_EXTENSION;
formats[0].proc = ReadNtk;
@@ -874,14 +922,16 @@ char *ReadNetlist(char *fname, int *fnum)
formats[1].proc = ReadExtHier;
formats[2].extension = SIM_EXTENSION;
formats[2].proc = ReadSim;
formats[3].extension = SPICE_EXTENSION;
formats[3].proc = ReadSpice;
formats[4].extension = NETGEN_EXTENSION;
formats[4].proc = ReadNetgenFile;
formats[5].extension = VERILOG_EXTENSION;
formats[5].proc = ReadVerilogFile;
formats[6].extension = NULL;
formats[6].proc = NULL;
formats[3].extension = PRM_EXTENSION;
formats[3].proc = ReadPrm;
formats[4].extension = SPICE_EXTENSION;
formats[4].proc = ReadSpice;
formats[5].extension = NETGEN_EXTENSION;
formats[5].proc = ReadNetgenFile;
formats[6].extension = VERILOG_EXTENSION;
formats[6].proc = ReadVerilogFile;
formats[7].extension = NULL;
formats[7].proc = NULL;
#else /* not mips (i.e. compiler with reasonable initializers) */
@@ -890,6 +940,7 @@ char *ReadNetlist(char *fname, int *fnum)
{NTK_EXTENSION, ReadNtk},
{EXT_EXTENSION, ReadExtHier},
{SIM_EXTENSION, ReadSim},
{PRM_EXTENSION, ReadPrm},
{SPICE_EXTENSION, ReadSpice},
{SPICE_EXT2, ReadSpice},
{SPICE_EXT3, ReadSpice},
+4
View File
@@ -7,6 +7,7 @@
#define WOMBAT_EXTENSION ".wom"
#define EXT_EXTENSION ".ext"
#define SIM_EXTENSION ".sim"
#define PRM_EXTENSION ".prm"
#define SPICE_EXTENSION ".spice"
#define SPICE_EXT2 ".spc"
#define SPICE_EXT3 ".sp"
@@ -35,6 +36,7 @@ extern struct hashdict *definitions;
extern char *nexttok;
#define SKIPTO(a) do {SkipTok(NULL);} while (!match(nexttok,a))
extern char *strdtok0(char *pstring, char *delim1, char *delim2, char isverilog);
extern char *strdtok(char *pstring, char *delim1, char *delim2);
extern char *GetLineAtTok();
extern void SkipTok(char *delimiter);
@@ -47,5 +49,7 @@ extern void InputParseError(FILE *f);
extern int OpenParseFile(char *name, int fnum);
extern int EndParseFile(void);
extern int CloseParseFile(void);
extern void SeekFile(long offset); /* handles verilog 'for' loops */
extern long TellFile(); /* handles verilog 'for' loops */
#endif /* _NETFILE_H */
+57 -5
View File
@@ -300,7 +300,9 @@ int ReduceOneExpression(struct valuelist *kv, struct objlist *parprops,
tstr = sstr - 1;
numlast = 1;
}
break;
/* But might not be. . . */
if ((dval != 0) || (sstr > estr))
break;
}
/* Not a number, so must be arithmetic */
*tstr = '\0';
@@ -320,7 +322,9 @@ int ReduceOneExpression(struct valuelist *kv, struct objlist *parprops,
tstr = sstr - 1;
numlast = 1;
}
break;
/* But might not be. . . */
if ((dval != 0) || (sstr > estr))
break;
}
/* Not a number, so must be arithmetic */
*tstr = '\0';
@@ -984,6 +988,51 @@ PropertyDelete(char *name, int fnum, char *key)
return 0;
}
/*----------------------------------------------------------------------*/
/* Associate a property with a specific pin */
/*----------------------------------------------------------------------*/
int
PropertyAssociatePin(char *name, int fnum, char *key, char *pin)
{
struct property *kl = NULL;
struct nlist *tc;
struct objlist *ob;
int result;
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
result = PropertyAssociatePin(name, Circuit1->file, key, pin);
result = PropertyAssociatePin(name, Circuit2->file, key, pin);
return result;
}
tc = LookupCellFile(name, fnum);
if (tc == NULL) {
Printf("No device %s found for PropertyAssociatePin()\n", name);
return -1;
}
kl = (struct property *)HashLookup(key, &(tc->propdict));
if (kl == NULL) {
Printf("No property %s found for device %s\n", key, name);
return -1;
}
else {
for (ob = tc->cell; ob != NULL; ob = ob->next) {
if (ob->type != PORT) break;
else if ((*matchfunc)(ob->name, pin)) {
kl->pin = ob->name;
break;
}
}
if (ob == NULL) {
Printf("No pin %s found for device %s\n", pin, name);
return -1;
}
}
return 0;
}
/*----------------------------------------------------------------------*/
/* Set the tolerance of a property in the master cell record. */
/*----------------------------------------------------------------------*/
@@ -2199,7 +2248,10 @@ int PromoteProperty(struct property *prop, struct valuelist *vl,
if (prop == NULL || vl == NULL) return -1;
if (prop->type == vl->type) return 1; /* Nothing to do */
result = 0;
if (prop->type == PROP_EXPRESSION) {
/* If vl is an expression but prop is not, then try to reduce
* the expression in vl.
*/
if (vl->type == PROP_EXPRESSION) {
ReduceOneExpression(vl, ob, tc, FALSE);
}
switch (prop->type) {
@@ -3045,7 +3097,7 @@ void add_balancing_close(struct objlist *ob1, struct objlist *ob2)
for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
if (nob->type == PROPERTY)
break;
if (nob->type != PROPERTY) return; // shouldn't happen
if (nob == NULL || nob->type != PROPERTY) return; // shouldn't happen
opentags = 0;
for (; nob->next && nob->next->type == PROPERTY; nob = nob->next) {
@@ -3595,7 +3647,7 @@ int CombineSeries(char *model, int file)
nob->type = PROPERTY;
nob->name = strsave("properties");
nob->node = -2; /* Don't report as disconnected node */
nob->model.class = (obp->model.class == NULL) ? NULL :
nob->model.class = (obp == NULL || obp->model.class == NULL) ? NULL :
strsave(obp->model.class);
nob->instance.props = NewPropValue(2);
+2
View File
@@ -39,6 +39,7 @@ extern void SetParallelCombine(int value);
extern void SetSeriesCombine(int value);
extern int PropertyTolerance(char *name, int fnum, char *key, int ival,
double dval);
extern int PropertyAssociatePin(char *name, int fnum, char *key, char *pin);
extern int PropertyMerge(char *name, int fnum, char *key, int merge_type,
int merge_mask);
extern void ResolveProperties(char *name1, int file1, char *name2, int file2);
@@ -190,6 +191,7 @@ extern char *ReadNtk (char *fname, int *fnum);
extern char *ReadExtHier(char *fname, int *fnum);
extern char *ReadExtFlat(char *fname, int *fnum);
extern char *ReadSim(char *fname, int *fnum);
extern char *ReadPrm(char *fname, int *fnum);
extern char *ReadSpice(char *fname, int *fnum);
extern char *ReadSpiceLib(char *fname, int *fnum);
extern char *ReadNetgenFile (char *fname, int *fnum);
+121 -101
View File
@@ -130,6 +130,7 @@ struct property *NewProperty(void)
kl = (struct property *)CALLOC(1,sizeof(struct property));
if (kl == NULL) Fprintf(stderr,"NewProperty: Core allocation error\n");
kl->pin = (char *)NULL;
return (kl);
}
@@ -222,12 +223,35 @@ int matchnocase(char *st1, char *st2)
{
char *sp1 = st1;
char *sp2 = st2;
char v1 = FALSE, v2 = FALSE;
/* In case of a property that does not exist in one netlist, matchnocase()
* may be passed a null value, so return 0 to indicate a non-match.
* *Both* values null will also be treated as a mismatch (debatable
* behavior).
*/
if (!sp1 || !sp2) return 0;
/* Verilog back-slash escaped names should match an equivalent non-
* back-slashed name. (NOTE: This behavior needs to be added to match().)
*/
if ((*sp1 == '\\') && (*sp2 != '\\')) {
v1 = TRUE;
sp1++;
}
if ((*sp2 == '\\') && (*sp1 != '\\')) {
v2 = TRUE;
sp2++;
}
while (*sp1 != '\0' && *sp2 != '\0') {
if (to_lower[*sp1] != to_lower[*sp2]) break;
sp1++;
sp2++;
}
if (v1 && (*sp1 == ' ')) sp1++;
if (v2 && (*sp2 == ' ')) sp2++;
if ((*sp1 != '\0') || (*sp2 != '\0')) return 0;
return 1;
}
@@ -440,13 +464,6 @@ void RemoveShorted(char *class, int file)
RecurseCellFileHashTable(removeshorted, file);
}
/* Structure used to keep track of nodes needing checking */
struct linkednode {
int node;
struct linkednode *next;
};
/* Remove instances of a deleted class from the database. */
/* NOTE: This treats deleted classes as not existing, so it */
/* needs to take care of disconnected ports in the same manner */
@@ -457,13 +474,22 @@ struct linkednode {
int deleteclass(struct hashlist *p, int file)
{
struct nlist *ptr;
struct objlist *ob, *lob, *nob;
struct linkednode *checknodes = NULL, *newlnode, *chknode;
struct objlist *ob, *lob, *nob, *portnode;
unsigned char *checknodes;
int i;
ptr = (struct nlist *)(p->ptr);
if ((file != -1) && (ptr->file != file)) return 0;
/* Note: This could be made faster by enumerating all times each
* node is used during the full pass, then subtracting each time
* the node is deleted in a child, then disconnecting all nodes
* that ended up with a zero count.
*/
checknodes = (unsigned char *)CALLOC(ptr->nodename_cache_maxnodenum + 1,
sizeof(unsigned char));
lob = NULL;
for (ob = ptr->cell; ob != NULL;) {
nob = ob->next;
@@ -471,12 +497,8 @@ int deleteclass(struct hashlist *p, int file)
if ((*matchfunc)(ob->model.class, OldCell->name)) {
HashDelete(ob->instance.name, &(ptr->instdict));
while (1) {
if (ob->type >= FIRSTPIN) {
newlnode = (struct linkednode *)MALLOC(sizeof(struct linkednode));
newlnode->node = ob->node;
newlnode->next = checknodes;
checknodes = newlnode;
}
if (ob->type >= FIRSTPIN)
checknodes[ob->node] = (unsigned char)1;
FreeObjectAndHash(ob, ptr);
ob = nob;
if (ob == NULL) break;
@@ -499,26 +521,24 @@ int deleteclass(struct hashlist *p, int file)
}
}
while (checknodes != NULL) {
struct objlist *portnode = NULL;
chknode = checknodes;
checknodes = checknodes->next;
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
if ((ob->type != PORT) && (portnode == NULL))
break;
else if ((ob->type == PORT) && (ob->node == chknode->node))
portnode = ob;
else if ((ob->type >= FIRSTPIN) && (ob->node == chknode->node))
break;
for (i = 0; i <= ptr->nodename_cache_maxnodenum; i++) {
if (checknodes[i] != 0) {
portnode = NULL;
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
if ((ob->type != PORT) && (portnode == NULL))
break;
else if ((ob->type == PORT) && (ob->node == i))
portnode = ob;
else if ((ob->type >= FIRSTPIN) && (ob->node == i))
break;
}
if ((ob == NULL) && (portnode != NULL)) {
/* Port became disconnected when child was deleted */
portnode->node = -1;
}
}
if ((ob == NULL) && (portnode != NULL)) {
/* Port became disconnected when child was deleted */
portnode->node = -1;
}
FREE(chknode);
}
FREE(checknodes);
}
/* Remove all instances of class "class" from the database */
@@ -599,65 +619,65 @@ int freeprop(struct hashlist *p)
void CellDelete(char *name, int fnum)
{
/* delete all the contents of cell 'name', and remove 'name' from
the cell hash table. NOTE: this procedure does not care or check
if 'name' has been instanced anywhere. It is assumed that if this
is the case, the user will (quickly) define a new cell of that name.
*/
struct objlist *ob, *obnext;
struct nlist *tp;
/* delete all the contents of cell 'name', and remove 'name' from
the cell hash table. NOTE: this procedure does not care or check
if 'name' has been instanced anywhere. It is assumed that if this
is the case, the user will (quickly) define a new cell of that name.
*/
struct objlist *ob, *obnext;
struct nlist *tp;
tp = LookupCellFile(name, fnum);
if (tp == NULL) {
Printf ("No cell '%s' found.\n", name);
return;
}
tp = LookupCellFile(name, fnum);
if (tp == NULL) {
Printf ("No cell '%s' found.\n", name);
return;
}
HashIntDelete(name, fnum, &cell_dict);
/* now make sure that we free all the fields of the nlist struct */
if (tp->name != NULL) FREE(tp->name);
HashKill(&(tp->objdict));
HashKill(&(tp->instdict));
RecurseHashTable(&(tp->propdict), freeprop);
HashKill(&(tp->propdict));
FreeNodeNames(tp);
ob = tp->cell;
while (ob != NULL) {
obnext = ob->next;
FreeObject (ob);
ob = obnext;
}
HashIntDelete(name, fnum, &cell_dict);
/* now make sure that we free all the fields of the nlist struct */
if (tp->name != NULL) FREE(tp->name);
HashKill(&(tp->objdict));
HashKill(&(tp->instdict));
RecurseHashTable(&(tp->propdict), freeprop);
HashKill(&(tp->propdict));
FreeNodeNames(tp);
ob = tp->cell;
while (ob != NULL) {
obnext = ob->next;
FreeObject (ob);
ob = obnext;
}
}
static int PrintCellHashTableElement(struct hashlist *p)
{
struct nlist *ptr;
struct nlist *ptr;
ptr = (struct nlist *)(p->ptr);
if ((TopFile >= 0) && (ptr->file != TopFile)) return 1;
ptr = (struct nlist *)(p->ptr);
if ((TopFile >= 0) && (ptr->file != TopFile)) return 1;
if (ptr->class != CLASS_SUBCKT) {
/* only print primitive cells if Debug is enabled */
if (Debug == 1) Printf("Cell: %s (instanced %d times); Primitive\n",
if ((ptr->class != CLASS_SUBCKT) && (ptr->class != CLASS_MODULE)) {
/* only print primitive cells if Debug is enabled */
if (Debug == 1) Printf("Cell: %s (instanced %d times); Primitive\n",
ptr->name, ptr->number);
else if (Debug == 3) { /* list */
else if (Debug == 3) { /* list */
#ifdef TCL_NETGEN
Tcl_AppendElement(netgeninterp, ptr->name);
Tcl_AppendElement(netgeninterp, ptr->name);
#else
Printf("%s ", ptr->name);
Printf("%s ", ptr->name);
#endif
}
}
else if ((Debug == 2) || (Debug == 3)) { /* list only */
}
}
else if ((Debug == 2) || (Debug == 3)) { /* list only */
#ifdef TCL_NETGEN
Tcl_AppendElement(netgeninterp, ptr->name);
Tcl_AppendElement(netgeninterp, ptr->name);
#else
Printf("%s ", ptr->name);
Printf("%s ", ptr->name);
#endif
}
else
Printf("Cell: %s (instanced %d times)\n",ptr->name,ptr->number);
return(1);
}
else
Printf("Cell: %s (instanced %d times)\n", ptr->name, ptr->number);
return(1);
}
/* Print the contents of the cell hash table. */
@@ -666,65 +686,65 @@ static int PrintCellHashTableElement(struct hashlist *p)
void PrintCellHashTable(int full, int filenum)
{
int total, bins;
int OldDebug;
int total, bins;
int OldDebug;
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
PrintCellHashTable(full, Circuit1->file);
PrintCellHashTable(full, Circuit2->file);
return;
}
}
TopFile = filenum;
TopFile = filenum;
bins = RecurseHashTable(&cell_dict, CountHashTableBinsUsed);
total = RecurseHashTable(&cell_dict, CountHashTableEntries);
if (full < 2)
Printf("Hash table: %d of %d bins used; %d cells total (%.2f per bin)\n",
bins = RecurseHashTable(&cell_dict, CountHashTableBinsUsed);
total = RecurseHashTable(&cell_dict, CountHashTableEntries);
if (full < 2)
Printf("Hash table: %d of %d bins used; %d cells total (%.2f per bin)\n",
bins, CELLHASHSIZE, total, (bins == 0) ? 0 :
(float)((float)total / (float)bins));
OldDebug = Debug;
Debug = full;
RecurseHashTable(&cell_dict, PrintCellHashTableElement);
Debug = OldDebug;
OldDebug = Debug;
Debug = full;
RecurseHashTable(&cell_dict, PrintCellHashTableElement);
Debug = OldDebug;
#ifndef TCL_NETGEN
if (full >= 2) Printf("\n");
if (full >= 2) Printf("\n");
#endif
}
struct nlist *FirstCell(void)
{
return((struct nlist *)HashFirst(&cell_dict));
return((struct nlist *)HashFirst(&cell_dict));
}
struct nlist *NextCell(void)
{
return((struct nlist *)HashNext(&cell_dict));
return((struct nlist *)HashNext(&cell_dict));
}
static int ClearDumpedElement(struct hashlist *np)
{
struct nlist *p;
struct nlist *p;
p = (struct nlist *)(np->ptr);
p->dumped = 0;
return(1);
p = (struct nlist *)(np->ptr);
p->dumped = 0;
return(1);
}
void ClearDumpedList(void)
{
RecurseHashTable(&cell_dict, ClearDumpedElement);
RecurseHashTable(&cell_dict, ClearDumpedElement);
}
int RecurseCellHashTable(int (*foo)(struct hashlist *np))
{
return RecurseHashTable(&cell_dict, foo);
return RecurseHashTable(&cell_dict, foo);
}
int RecurseCellFileHashTable(int (*foo)(struct hashlist *, int), int value)
{
return RecurseHashTableValue(&cell_dict, foo, value);
return RecurseHashTableValue(&cell_dict, foo, value);
}
/* Yet another version, passing one parameter that is a pointer */
@@ -732,7 +752,7 @@ int RecurseCellFileHashTable(int (*foo)(struct hashlist *, int), int value)
struct nlist *RecurseCellHashTable2(struct nlist *(*foo)(struct hashlist *,
void *), void *pointer)
{
return RecurseHashTablePointer(&cell_dict, foo, pointer);
return RecurseHashTablePointer(&cell_dict, foo, pointer);
}
/************************** WILD-CARD STUFF *******************************/
+11 -9
View File
@@ -135,6 +135,7 @@ struct property {
unsigned char idx; /* index into valuelist */
unsigned char type; /* string, integer, double, value, expression */
unsigned char merge; /* how property changes when devices are merged */
char *pin; /* associated pin (or NULL if not associated) */
union {
char *string;
double dval;
@@ -206,7 +207,7 @@ struct nlist {
char *name;
int number; /* number of instances defined */
int dumped; /* instance count, and general-purpose marker */
unsigned char flags;
unsigned short flags;
unsigned char class;
unsigned long classhash; /* randomized hash value for cell class */
struct Permutation *permutes; /* list of permuting pins */
@@ -222,17 +223,18 @@ struct nlist {
/* Defined nlist structure flags */
#define CELL_MATCHED 0x01 /* cell matched to another */
#define CELL_NOCASE 0x02 /* cell is case-insensitive (e.g., SPICE) */
#define CELL_TOP 0x04 /* cell is a top-level cell */
#define CELL_PLACEHOLDER 0x08 /* cell is a placeholder cell */
#define CELL_PROPSMATCHED 0x10 /* properties matched to matching cell */
#define CELL_DUPLICATE 0x20 /* cell has a duplicate */
#define CELL_MATCHED 0x001 /* cell matched to another */
#define CELL_NOCASE 0x002 /* cell is case-insensitive (e.g., SPICE) */
#define CELL_TOP 0x004 /* cell is a top-level cell */
#define CELL_PLACEHOLDER 0x008 /* cell is a placeholder cell */
#define CELL_PROPSMATCHED 0x010 /* properties matched to matching cell */
#define CELL_DUPLICATE 0x020 /* cell has a duplicate */
#define CELL_VERILOG 0x040 /* cell is verilog module */
/* Flags for combination allowances and prohibitions */
#define COMB_SERIES 0x40
#define COMB_NO_PARALLEL 0x80
#define COMB_SERIES 0x100
#define COMB_NO_PARALLEL 0x200
extern struct nlist *CurrentCell;
extern struct objlist *CurrentTail;
+98 -8
View File
@@ -43,6 +43,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "print.h"
#include "query.h"
#include "objlist.h"
#include "netcmp.h"
// Global storage for parameters from .PARAM
struct hashdict spiceparams;
@@ -519,7 +520,7 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
char *eqptr, devtype, in_subckt;
struct keyvalue *kvlist = NULL;
char inst[MAX_STR_LEN], model[MAX_STR_LEN], instname[MAX_STR_LEN];
struct nlist *tp;
struct nlist *tp, *tpsave;
struct objlist *parent, *sobj, *nobj, *lobj, *pobj;
inst[MAX_STR_LEN-1] = '\0';
@@ -531,8 +532,14 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
SkipTok(NULL); /* get the next token */
if ((EndParseFile()) && (nexttok == NULL)) break;
if (nexttok == NULL) break;
if (nexttok[0] == '*') SkipNewLine(NULL);
/* Handle comment lines. Note that some variants of CDL format
* use "*." for information that is transparent to SPICE simulators.
* Handle "*.GLOBAL" entries. All others are ignored.
*/
if ((nexttok[0] == '*') && (!matchnocase(nexttok, "*.GLOBAL")))
SkipNewLine(NULL);
else if (matchnocase(nexttok, ".SUBCKT")) {
SpiceTokNoNewline();
@@ -557,6 +564,7 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
snprintf(model, MAX_STR_LEN-1, "%s", nexttok);
tp = LookupCellFile(nexttok, filenum);
tpsave = NULL;
/* Check for name conflict with duplicate cell names */
/* This may mean that the cell was used before it was */
@@ -595,11 +603,46 @@ void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
tp = LookupCellFile(nexttok, filenum);
}
else if (tp != NULL) { /* Make a new definition for an empty cell */
FreePorts(nexttok);
CellDelete(nexttok, filenum); /* This removes any PLACEHOLDER flag */
CellDef(model, filenum);
tp = LookupCellFile(model, filenum);
update = 1; /* Will need to update existing instances */
/* Handle issue with SPICE read after verilog, where a placeholder
* was created from the verilog. (1) If the pin names are "1", "2",
* "3", then this is a SPICE placeholder, and just remove the CellDef
* and re-create it. Otherwise, create new cell "_PLACEHOLDER_".
* (2) After encountering .ends, run MatchPins between the two cells.
* (3) delete the original cell and rename the new cell.
*/
int i = 1;
char pname[10];
for (pobj = tp->cell; pobj && pobj->type == PORT; pobj = pobj->next) {
sprintf(pname, "%d", i);
if (!matchnocase(pobj->name, pname)) break;
i++;
}
if ((pobj == NULL) || (pobj->type != PORT)) {
/* This is a SPICE placeholder created because the cell was instanced
* before it was defined. However, the pins can be assumed to be in
* the correct order, and pin reordering does not need to be done.
*/
FreePorts(nexttok);
CellDelete(nexttok, filenum); /* This removes any PLACEHOLDER flag */
CellDef(model, filenum);
tp = LookupCellFile(model, filenum);
update = 1; /* Will need to update existing instances */
}
else {
/* This is (probably) a verilog placeholder created because the
* verilog was read before the (SPICE) definitions. The verilog
* netlist should have named the pins of the parent cell. However,
* there is no guarantee the order of pins is correct. The MatchPins()
* routine from netcmp.c can be used here to match the cell against
* the placeholder, and reorder the pins in all instances to match.
* Note that we cannot just reorder the SPICE pins to match the
* verilog order, because there may be other SPICE netlists which
* instance the cell with the correct SPICE port order.
*/
tpsave = tp;
CellDef("_PLACEHOLDER_", filenum);
tp = LookupCellFile("_PLACEHOLDER_", filenum);
}
}
else if (tp == NULL) { /* Completely new cell, no name conflict */
CellDef(model, filenum);
@@ -691,6 +734,38 @@ skip_ends:
if (*CellStackPtr) PopStack(CellStackPtr);
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
SkipNewLine(NULL);
if (tpsave != NULL) {
struct nlist *tpplace;
char *savename;
/* Handle a placeholder from a verilog file that has been replaced
* by a netlist with pins in a different order. The pins need to
* be matched, corrected in the original cell and all instances,
* and the new cell deleted.
*/
Printf("Verilog placeholder %s replaced by SPICE definition\n",
tpsave->name);
tpplace = LookupCellFile("_PLACEHOLDER_", filenum);
/* MatchPins is part of netcmp and normally Circuit2 is the
* circuit being matched, so set Circuit2 to the original
* verilog black-box cell, and MatchPins() will force its
* pins to be rearranged to match the SPICE definition just
* read.
*/
Circuit2 = tpsave;
MatchPins(tpplace, tpsave, 0);
savename = strsave(tpsave->name);
/* Now the original verilog black-box cell can be removed */
FreePorts(savename);
CellDelete(savename, filenum);
/* And _PLACEHOLDER_ is renamed to the original name of the cell. */
CellRehash("_PLACEHOLDER_", savename, filenum);
tpsave = NULL;
Circuit2 = NULL;
FREE(savename);
}
}
else if (matchnocase(nexttok, ".MODEL")) {
unsigned char class = CLASS_SUBCKT;
@@ -752,7 +827,10 @@ skip_ends:
// Handle some commonly-used cards
else if (matchnocase(nexttok, ".GLOBAL")) {
/* .GLOBAL and *.GLOBAL. Note that *.GLOBAL is excepted from comment-line
* handling, above, so any line starting with '*' is "*.GLOBAL".
*/
else if (matchnocase(nexttok, ".GLOBAL") || (nexttok[0] == '*')) {
while (nexttok != NULL) {
int numnodes = 0;
SpiceTokNoNewline();
@@ -1783,6 +1861,18 @@ skip_ends:
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
update = 1;
}
else if (tp->flags & CELL_VERILOG) {
if (tp->flags & CELL_PLACEHOLDER) {
/* Flag this as an error. To do: Rearrange the verilog instance pins to */
/* match the SPICE subcircuit pin order. */
Fprintf(stderr, "Error: SPICE subcircuit %s should be read before verilog "
"module using it, or pins may not match!\n", subcktname);
}
else {
Fprintf(stderr, "Error: SPICE subcircuit %s redefines a verilog module!\n",
subcktname);
}
}
/* nexttok is now NULL, scan->name points to class */
+2
View File
@@ -34,6 +34,8 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "print.h"
#endif
extern void Finsert(FILE *f);
void test_entry(void)
{
+544 -41
View File
@@ -42,6 +42,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <stdlib.h> /* for calloc(), free(), getenv() */
#include <ctype.h> /* for isalnum() */
#ifndef IBMPC
#include <sys/types.h> /* for getpwnam() tilde expansion */
#include <pwd.h>
@@ -56,6 +57,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "netfile.h"
#include "print.h"
#include "hash.h"
#include "netcmp.h"
// See netfile.c for explanation of delimiters. 'X'
// separates single-character delimiters from two-character delimiters.
@@ -63,6 +65,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#define VLOG_EQUATION_DELIMITERS "X///**/#((**)X,;:(){}[]=+-*/"
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
#define VLOG_INTEGER_DELIMITERS "X///**/X;[]"
// Used by portelement structure "flags" record.
#define PORT_NOT_FOUND 0
@@ -83,6 +86,16 @@ struct bus {
int end;
};
// Global storage for a 'for' loop
struct _loop {
char *loopvar;
int start;
int end;
long filepos;
};
struct _loop loop;
// Free a bus structure in the hash table during cleanup
int freebus (struct hashlist *p)
@@ -244,7 +257,16 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
for (texp = start; texp; texp = texp->next) {
if ((texp->last != NULL) && (texp->next != NULL) && (texp->oper != '\0')) {
if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
/* Watch for (a)*b+c or a+b*(c); multiplies must be solved first */
if ((texp->last->last != NULL) && ((texp->last->last->oper == '*')
|| (texp->last->last->oper == '/'))) {
/* Do nothing */
}
else if ((texp->next->next != NULL) && ((texp->next->next->oper == '*')
|| (texp->next->next->oper == '/'))) {
/* Do nothing */
}
else if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
if (texp->oper == '-') {
/* Subtract */
texp->last->value -= texp->next->value;
@@ -290,6 +312,7 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
}
}
}
}
/* If only one numerical item remains, then place it in valptr and return 1 */
@@ -311,6 +334,134 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
return 0;
}
//-------------------------------------------------------------------------
// Parse an expression which must resolve to a single integer.
// If it does, return "1" and put the integer in the return
// pointer "iptr". If not, return "0" and iptr is undefined.
// The expression may use parameters, standard arithmetic,
// and parenthetic grouping. This routine does not tokenize input
// but assumes that the entire expression is in the string "expr".
//-------------------------------------------------------------------------
int ParseIntegerExpression(char *expr, int *iptr)
{
int result, value;
char *sptr, *cptr, savec;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
stack = NULL;
savec = '\0';
sptr = expr;
result = 1;
while (sptr && (*sptr != '\0')) {
// Move sptr to first non-space character
while (isspace(*sptr)) sptr++;
// Tokenize. Look ahead to next delimeter and truncate string there.
cptr = sptr + 1;
if (isalnum(*sptr) || (*sptr == '_') || (*sptr == '$')) {
while (*cptr != '\0') {
if (isalnum(*cptr)) cptr++;
else if ((*cptr == '_') || (*cptr == '$')) cptr++;
else break;
}
}
savec = *cptr;
*cptr = '\0';
if (match(sptr, "+") || match(sptr, "-")
|| match(sptr, "*") || match(sptr, "/")
|| match(sptr, "(") || match(sptr, ")")) {
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
newexp->oper = *sptr;
newexp->value = 0;
newexp->next = NULL;
newexp->last = stack;
if (stack) stack->next = newexp;
stack = newexp;
sptr = cptr;
*cptr = savec;
continue;
}
if ((result = sscanf(sptr, "%d", &value)) != 1) {
// Is name in the parameter list?
kl = (struct property *)HashLookup(nexttok, &verilogparams);
if (kl == NULL) {
Printf("Value %s in expression is not a number or a parameter.\n",
sptr);
value = 0;
break;
}
else {
if (kl->type == PROP_STRING) {
result = sscanf(kl->pdefault.string, "%d", &value);
if (result != 1) {
Printf("Parameter %s has value %s that cannot be parsed"
" as an integer.\n",
nexttok, kl->pdefault.string);
value = 0;
break;
}
}
else if (kl->type == PROP_INTEGER) {
value = kl->pdefault.ival;
}
else if (kl->type == PROP_DOUBLE) {
value = (int)kl->pdefault.dval;
if ((double)value != kl->pdefault.dval) {
Printf("Parameter %s has value %g that cannot be parsed"
" as an integer.\n",
nexttok, kl->pdefault.dval);
value = 0;
break;
}
}
else {
Printf("Parameter %s has unknown type; don't know how"
" to parse.\n", nexttok);
value = 0;
break;
}
}
}
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
newexp->oper = '\0';
newexp->value = value;
newexp->next = NULL;
newexp->last = stack;
if (stack) stack->next = newexp;
stack = newexp;
/* Move to next token */
sptr = cptr;
*cptr = savec;
}
if (result != 0) {
if (stack == NULL) {
Printf("Empty array found.\n");
result = 0;
}
else if (EvalExpr(&stack, iptr) != 1) {
Printf("Bad expression found in array.\n");
result = 0;
}
}
/* In case of error, stack may need cleaning up */
while (stack != NULL) {
newexp = stack;
stack = stack->last;
FREE(newexp);
}
return result;
}
//-------------------------------------------------------------------------
// Get bus indexes from the notation name[a:b]. If there is only "name"
// then look up the name in the bus hash list and return the index bounds.
@@ -323,7 +474,6 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
int GetBusTok(struct bus *wb)
{
int result, start, end, value;
char oper;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
@@ -338,7 +488,6 @@ int GetBusTok(struct bus *wb)
if (match(nexttok, "[")) {
start = end = -1;
oper = '\0';
while (nexttok) {
SkipTokComments(VLOG_EQUATION_DELIMITERS);
if (match(nexttok, "]")) {
@@ -467,13 +616,16 @@ int GetBusTok(struct bus *wb)
//--------------------------------------------------------------------
// GetBus() is similar to GetBusTok() (see above), but it parses from
// a string instead of the input tokenizer.
// a string instead of the input tokenizer, and expressions are not
// allowed.
//--------------------------------------------------------------------
int GetBus(char *astr, struct bus *wb)
{
char *colonptr, *brackstart, *brackend, *sigend, sdelim, *aastr;
int result, start, end;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
if (wb == NULL) return 0;
else {
@@ -531,7 +683,7 @@ int GetBus(char *astr, struct bus *wb)
*brackend = '\0';
colonptr = strvchr(aastr, ':');
if (colonptr) *colonptr = '\0';
result = sscanf(brackstart + 1, "%d", &start);
result = ParseIntegerExpression(brackstart + 1, &start);
if (colonptr) *colonptr = ':';
if (result != 1) {
Printf("Badly formed array notation \"%s\"\n", astr);
@@ -539,7 +691,7 @@ int GetBus(char *astr, struct bus *wb)
return 1;
}
if (colonptr)
result = sscanf(colonptr + 1, "%d", &end);
result = ParseIntegerExpression(colonptr + 1, &end);
else {
result = 1;
end = start; // Single bit
@@ -565,10 +717,12 @@ int GetBus(char *astr, struct bus *wb)
return 0;
}
//--------------------------------------------------------------------
// Output a Verilog Module. Note that since Verilog does not describe
// low-level devices like transistors, capacitors, etc., then this
// format is limited to black-box subcircuits. Cells containing any
// such low-level devices are ignored.
//--------------------------------------------------------------------
void VerilogModule(struct nlist *tp)
{
@@ -803,6 +957,56 @@ extern void IncludeVerilog(char *, int, struct cellstack **, int);
extern void PushStack(char *cellname, struct cellstack **top);
extern void PopStack(struct cellstack **top);
/*------------------------------------------------------*/
/* Callback routine for FindInstanceOf() */
/* NOTE: This casts a (struct objlist) pointer to a */
/* (struct nlist) pointer for the purpose of using */
/* RecurseCellHashTable2(). FindInstanceOf() casts it */
/* back into a (struct objlist) pointer. */
/*------------------------------------------------------*/
struct nlist *findInstance(struct hashlist *p, void *clientdata)
{
struct nlist *ptr;
struct objlist *ob;
struct nlist *tref = (struct nlist *)clientdata;
ptr = (struct nlist *)(p->ptr);
if (ptr->file != tref->file) return NULL;
ob = LookupInstance(tref->name, ptr);
return (struct nlist *)ob;
}
/*------------------------------------------------------*/
/* Routine to find the first instance of a cell */
/*------------------------------------------------------*/
struct objlist *FindInstanceOf(struct nlist *tc)
{
return (struct objlist *)RecurseCellHashTable2(findInstance, (void *)tc);
}
/*------------------------------------------------------*/
/* Given a reference cell pointer tref and a port name */
/* portname, check if portname is a port of tref. If */
/* not, then call Port() to add one. If tref is NULL, */
/* then always add the port. */
/*------------------------------------------------------*/
void CheckPort(struct objlist *tref, char *portname)
{
struct objlist *ob;
if (tref != NULL) {
for (ob = CurrentCell->cell; ob && (ob->type == PORT); ob = ob->next) {
if ((*matchfunc)(ob->name, portname))
return;
}
}
Port(portname);
}
/*------------------------------------------------------*/
/* Read a verilog structural netlist */
/*------------------------------------------------------*/
@@ -816,16 +1020,17 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
char devtype, in_module, in_param;
char *eqptr, *matchptr;
struct keyvalue *kvlist = NULL;
char inst[MAX_STR_LEN], model[MAX_STR_LEN], instname[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN];
struct nlist *tp;
struct objlist *parent, *sobj, *nobj, *lobj, *pobj;
char inst[MAX_STR_LEN], model[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN];
struct nlist *tp, *tpsave;
struct objlist *parent, *sobj, *nobj, *lobj, *pobj, *cref;
inst[MAX_STR_LEN-1] = '\0';
model[MAX_STR_LEN-1] = '\0';
instname[MAX_STR_LEN-1] = '\0';
in_module = (char)0;
in_param = (char)0;
loop.loopvar = NULL;
while (!EndParseFile()) {
SkipTokComments(VLOG_DELIMITERS); /* get the next token */
@@ -938,6 +1143,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
InputParseError(stderr);
}
in_module = (char)1;
cref = NULL;
/* Save pointer to current cell */
if (CurrentCell != NULL)
@@ -949,6 +1155,8 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
snprintf(model, MAX_STR_LEN-1, "%s", nexttok);
tp = LookupCellFile(nexttok, filenum);
tpsave = NULL;
hasports = (char)0;
/* Check for name conflict with duplicate cell names */
/* This may mean that the cell was used before it was */
@@ -986,23 +1194,22 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
CellDef(nexttok, filenum);
tp = LookupCellFile(nexttok, filenum);
}
else if (tp != NULL) { /* Make a new definition for an empty cell */
FreePorts(nexttok);
CellDelete(nexttok, filenum); /* This removes any PLACEHOLDER flag */
CellDef(model, filenum);
tp = LookupCellFile(model, filenum);
else if (tp != NULL) { /* Cell exists, but as a placeholder */
tpsave = tp;
CellDef("_PLACEHOLDER_", filenum);
tp = LookupCellFile("_PLACEHOLDER_", filenum);
}
else if (tp == NULL) { /* Completely new cell, no name conflict */
CellDef(model, filenum);
tp = LookupCellFile(model, filenum);
}
hasports = (char)0;
inlined_decls = (char)0;
if (tp != NULL) {
struct bus wb, *nb;
tp->flags |= CELL_VERILOG;
PushStack(tp->name, CellStackPtr);
/* Need to support both types of I/O lists: Those */
@@ -1071,7 +1278,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
if (GetBusTok(&wb) != 0) {
// Didn't parse as a bus, so wing it
wb.start = wb.end = -1;
Port(nexttok);
CheckPort(cref, nexttok);
}
}
else {
@@ -1079,13 +1286,13 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
if (wb.start > wb.end) {
for (i = wb.start; i >= wb.end; i--) {
sprintf(portname, "%s[%d]", nexttok, i);
Port(portname);
CheckPort(cref, portname);
}
}
else {
for (i = wb.start; i <= wb.end; i++) {
sprintf(portname, "%s[%d]", nexttok, i);
Port(portname);
CheckPort(cref, portname);
}
}
/* Also register this port as a bus */
@@ -1097,7 +1304,7 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
wb.start = wb.end = -1;
}
else {
Port(nexttok);
CheckPort(cref, nexttok);
}
}
hasports = 1;
@@ -1153,7 +1360,7 @@ skip_endmodule:
if (GetBusTok(&wb) != 0) {
// Didn't parse as a bus, so wing it
wb.start = wb.end = -1;
Port(nexttok);
CheckPort(cref, nexttok);
}
}
else if (!match(nexttok, ",")) {
@@ -1161,13 +1368,13 @@ skip_endmodule:
if (wb.start > wb.end) {
for (i = wb.start; i >= wb.end; i--) {
sprintf(portname, "%s[%d]", nexttok, i);
Port(portname);
CheckPort(cref, portname);
}
}
else {
for (i = wb.start; i <= wb.end; i++) {
sprintf(portname, "%s[%d]", nexttok, i);
Port(portname);
CheckPort(cref, portname);
}
}
/* Also register this port as a bus */
@@ -1178,7 +1385,7 @@ skip_endmodule:
wb.start = wb.end = -1;
}
else {
Port(nexttok);
CheckPort(cref, nexttok);
}
}
hasports = 1;
@@ -1194,10 +1401,98 @@ skip_endmodule:
InputParseError(stderr);
}
in_module = (char)0;
cref = NULL;
if (*CellStackPtr) PopStack(CellStackPtr);
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
SkipNewLine(VLOG_DELIMITERS);
if (tpsave != NULL) {
struct nlist *tpplace;
char *savename;
int lnum, pnum, ltest;
unsigned char valid;
struct objlist *lobj, *pobj;
/* Handle a placeholder from a verilog file that has been replaced
* by a netlist with pins in a different order. The pins need to
* be matched, corrected in the original cell and all instances,
* and the new cell deleted.
*/
Printf("Verilog placeholder module %s replaced by module definition\n",
tpsave->name);
tpplace = LookupCellFile("_PLACEHOLDER_", filenum);
/* If tpsave was generated from an instance in a SPICE netlist that
* did not have a black-box subcircuit definition, then the pins
* will all be labeled 1, 2, 3, etc. If so, then assume that the
* verilog pins are in order, and rename the placeholder pins.
* If the number of pins does not match, or if the pins are not
* labeled as ascending integers, then leave the cell alone.
* In either case, output a warning message.
*/
/* Get the number of ports in the placeholder */
pnum = 0;
for (pobj = tpplace->cell; pobj; pobj = pobj->next) {
if (pobj->type != PORT) break;
pnum++;
}
/* Get the number of ports in the saved cell and make */
/* sure that it equals the number of ports in the */
/* placeholder, and that all of the ports in the saved */
/* cell are integers in ascending order. */
valid = TRUE;
lnum = 0;
for (lobj = tpsave->cell; lobj; lobj = lobj->next) {
if (lobj->type != PORT) break;
lnum++;
if (sscanf(lobj->name, "%d", &ltest) != 1) break;
if (ltest != lnum) break;
}
if ((lobj != NULL) && (lobj->type == PORT))
valid = FALSE; /* Pins are not integers in ascending order */
if (pnum != lnum) valid = FALSE; /* Different number of pins */
if (valid == TRUE) {
Printf("Replacing pins of placeholder cell %s from cell definition.\n",
tpsave->name);
pobj = tpplace->cell;
for (lobj = tpsave->cell; lobj; lobj = lobj->next) {
if (lobj->type != PORT) break;
if (pobj == NULL) break; /* should not happen */
FREE(lobj->name);
lobj->name = (char *)MALLOC(strlen(pobj->name) + 1);
strcpy(lobj->name, pobj->name);
pobj = pobj->next;
}
}
else {
Printf("Placeholder pins of cell %s are not compatible and"
" will be left unchanged\n", tpsave->name);
}
/* MatchPins is part of netcmp and normally Circuit2 is the
* circuit being matched, so set Circuit2 to the original
* verilog black-box cell, and MatchPins() will force its
* pins to be rearranged to match the module definition just
* read.
*/
Circuit2 = tpsave;
MatchPins(tpplace, tpsave, 0);
savename = strsave(tpsave->name);
/* Now the original verilog black-box cell can be removed */
FreePorts(savename);
CellDelete(savename, filenum);
/* And _PLACEHOLDER_ is renamed to the original name of the cell. */
CellRehash("_PLACEHOLDER_", savename, filenum);
tpsave = NULL;
Circuit2 = NULL;
FREE(savename);
}
}
else if (match(nexttok, "`include")) {
@@ -1338,6 +1633,145 @@ skip_endmodule:
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
continue;
}
else if (match(nexttok, "end")) {
/* Handle a 'for' loop */
if (loop.loopvar != NULL) {
int loopval;
struct property *klr;
klr = (struct property *)HashLookup(loop.loopvar, &verilogparams);
loopval = klr->pdefault.ival;
if (loopval < loop.end) loopval++;
else if (loopval > loop.end) loopval--;
SeekFile(loop.filepos);
if (loopval == loop.end)
{
HashDelete(loop.loopvar, &verilogparams);
FREE(klr);
FREE(loop.loopvar);
loop.loopvar = NULL;
}
else
klr->pdefault.ival = loopval;
}
continue;
}
else if (match(nexttok, "begin") || match(nexttok, "generate")) {
/* 'generate' section or 'for' loop start is ignored */
continue;
}
else if (match(nexttok, "generate") || match(nexttok, "endgenerate")) {
/* 'generate' section is ignored */
continue;
}
else if (match(nexttok, "genvar")) {
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
continue;
}
else if (match(nexttok, "for")) {
char limittype = '\0';
char *paramkey = NULL;
struct property *kl = NULL, *klr;
/* Parse out the loop variable and count. Set the loop
* variable as a parameter. Find the beginning of the
* loop block and record the file position so that the
* block can be re-parsed for each loop iteration.
*/
SkipTokNoNewline(VLOG_DELIMITERS);
if ((nexttok == NULL) || !match(nexttok, "(")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
break;
}
SkipTokNoNewline(VLOG_DELIMITERS);
/* Next token must be the loop variable */
if (nexttok == NULL) break;
paramkey = strsave(nexttok);
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, "=")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
break;
}
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
if (ParseIntegerExpression(nexttok, &ival) == 0) {
FREE(paramkey);
break;
}
kl = NewProperty();
kl->type = PROP_INTEGER;
kl->slop.ival = 0;
kl->pdefault.ival = ival;
kl->key = paramkey;
kl->idx = 0;
kl->merge = MERGE_NONE;
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, ";")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Assuming a standard 'for' loop here */
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, paramkey)) {
Printf("Don't know how to parse this 'for' loop!\n");
FREE(paramkey);
FREE(kl);
break;
}
SkipTokNoNewline(VLOG_DELIMITERS);
if (strlen(nexttok) == 1) limittype = *nexttok;
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
if (ParseIntegerExpression(nexttok, &ival) == 0) {
FREE(kl);
FREE(paramkey);
break;
}
/* Loops will stop after the last value, so if value is > or <,
* then adjust the end value accordingly.
*/
if (limittype == '<')
ival--;
else if (limittype == '>')
ival++;
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, ";")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Assume a standard 'for' loop and skip to the block begin */
/* To do: Parse out the loop increment value. */
while (1) {
SkipTokNoNewline(VLOG_DELIMITERS);
if (EndParseFile()) break;
else if (match(nexttok, "begin")) break;
}
if (EndParseFile()) {
Printf("Badly formed 'for' loop: No begin/end block.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Save the loop variable, ending value, and file position */
loop.loopvar = paramkey;
loop.start = kl->pdefault.ival;
loop.end = ival;
loop.filepos = TellFile();
/* 'for' loop has been completely parsed, so save the loop variable */
HashPtrInstall(paramkey, kl, &verilogparams);
}
else if (match(nexttok, "wire") || match(nexttok, "assign")) { /* wire = node */
struct bus wb, wb2, *nb;
char nodename[MAX_STR_LEN], noderoot[MAX_STR_LEN];
@@ -1401,8 +1835,9 @@ skip_endmodule:
}
else { /* "assign" */
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
if (GetBus(nexttok, &wb) == 0) {
char *aptr = strvchr(nexttok, '[');
char *aptr = strvchr(nexttok, '[');
if (((aptr == NULL) && (GetBusTok(&wb) == 0)) ||
((aptr != NULL) && (GetBus(aptr, &wb) == 0))) {
if (aptr != NULL) {
*aptr = '\0';
/* Find object of first net in bus */
@@ -1420,6 +1855,15 @@ skip_endmodule:
}
else {
lhs = LookupObject(nexttok, CurrentCell);
/* Handle the case in which an assignment is made
* without first declaring a wire for the signal,
* which is considered valid syntax (patch by
* Sylvain Munaut).
*/
if (lhs == NULL) {
Node(nexttok);
lhs = LookupObject(nexttok, CurrentCell);
}
strcpy(noderoot, nexttok);
}
SkipTokComments(VLOG_DELIMITERS);
@@ -1436,9 +1880,11 @@ skip_endmodule:
// "assign a = b" joins two nets.
// "assign a = {b, c, ...}" creates a bus from components.
// "assign" using any boolean arithmetic is not structural verilog.
// "assign a = {x{b}}" creates a bus by repeating a component.
if (nexttok && match(nexttok, "=")) {
char assignname[MAX_STR_LEN], assignroot[MAX_STR_LEN];
int multiplier = 1;
i = wb.start;
while (1) {
@@ -1447,6 +1893,10 @@ skip_endmodule:
if (match(nexttok, "{")) {
/* RHS is a bundle */
/* Make provisional multiplier active. NOTE: */
/* this is not going to properly handle complex */
/* multipliers like {2{a,b}}. Needs rework. */
if (multiplier < 0) multiplier = -multiplier;
continue;
}
else if (match(nexttok, "}")) {
@@ -1462,8 +1912,9 @@ skip_endmodule:
break;
}
else {
if (GetBus(nexttok, &wb2) == 0) {
char *aptr = strvchr(nexttok, '[');
char *aptr = strvchr(nexttok, '[');
if (((aptr == NULL) && (GetBusTok(&wb2) == 0)) ||
((aptr != NULL) && (GetBus(aptr, &wb2) == 0))) {
j = wb2.start;
if (aptr != NULL) {
*aptr = '\0';
@@ -1478,6 +1929,18 @@ skip_endmodule:
else {
j = -1;
rhs = LookupObject(nexttok, CurrentCell);
/* Check if rhs starts with a signal multiplier */
if (rhs == NULL) {
if (ConvertStringToInteger(nexttok, &multiplier) == 1) {
/* Set multiplier to a negative value to
* indicate that it is provisional, waiting on
* signal specification to follow.
*/
multiplier = -multiplier;
continue;
}
}
}
if ((lhs == NULL) || (rhs == NULL)) {
if (rhs != NULL) {
@@ -1490,7 +1953,7 @@ skip_endmodule:
Printf("Improper expression is \"%s\".\n", nexttok);
break;
}
if (lhs != NULL) {
if ((lhs != NULL) || (multiplier < 0)) {
Printf("Improper assignment; right-hand side cannot "
"be parsed.\n");
if (i != -1)
@@ -1510,21 +1973,36 @@ skip_endmodule:
/* until bits in signal are exhausted or LHS is full. */
if (i != -1)
sprintf(nodename, "%s[%d]", noderoot, i);
else
sprintf(nodename, lhs->name);
snprintf(nodename, MAX_STR_LEN, "%s[%d]", noderoot, i);
else if (lhs != NULL)
strncpy(nodename, lhs->name, MAX_STR_LEN - 1);
else {
Printf("Error: Improper node name \"%s\".\n", noderoot);
goto skip_endmodule;
}
if (j != -1)
sprintf(assignname, "%s[%d]", assignroot, j);
else
sprintf(assignname, rhs->name);
snprintf(assignname, MAX_STR_LEN, "%s[%d]", assignroot, j);
else if (rhs != NULL)
strncpy(assignname, rhs->name, MAX_STR_LEN - 1);
else {
Printf("Error: Improper assignment name \"%s\".\n",
assignroot);
goto skip_endmodule;
}
join(nodename, assignname);
if (i == wb.end) break;
i += (wb.end > wb.start) ? 1 : -1;
if (j == wb2.end) break;
j += (wb2.end > wb2.start) ? 1 : -1;
if (j == wb2.end) {
if (multiplier <= 1)
break;
else
multiplier--;
}
else
j += (wb2.end > wb2.start) ? 1 : -1;
}
}
}
@@ -1732,6 +2210,31 @@ nextinst:
}
new_port->net = wire_bundle;
}
else if ((strchr(nexttok, '[') != NULL) &&
(strchr(nexttok, ']') == NULL)) {
/* If a bus expressions has whitespace, then treat it like
* a bundle, above, concatenating to the closing ']'.
*/
char *array_expr = (char *)MALLOC(1);
char *new_array_expr = NULL;
*array_expr = '\0';
/* Read to "]" */
while (nexttok) {
new_array_expr = (char *)MALLOC(strlen(array_expr) +
strlen(nexttok) + 1);
/* Roundabout way to do realloc() becase there is no REALLOC() */
strcpy(new_array_expr, array_expr);
strcat(new_array_expr, nexttok);
FREE(array_expr);
array_expr = new_array_expr;
if (strchr(nexttok, ']')) break;
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
}
if (!nexttok) {
Printf("Unterminated net in pin %s\n", array_expr);
}
new_port->net = array_expr;
}
else if (nexttok[0] == '~' || nexttok[0] == '!' || nexttok[0] == '-') {
/* All of these imply that the signal is logically manipulated */
/* in turn implying behavioral code. */
@@ -2115,7 +2618,7 @@ nextinst:
sprintf(localnet, "_noconnect_%d_", localcount++);
Node(localnet);
join(localnet, obptr->name);
Fprintf(stderr,
Fprintf(stdout,
"Note: Implicit pin %s in instance %s of %s in cell %s\n",
obpinname, locinst, modulename, CurrentCell->name);
}
@@ -2207,8 +2710,8 @@ nextinst:
char tempname[MAX_STR_LEN];
int maxnode;
/* This pin was probably implicit in the first call */
/* and so it needs to be added to the definition. */
/* This pin was probably implicit in the first call */
/* and so it needs to be added to the definition. */
ReopenCellDef(modulename, filenum);
Port(scan->name);
@@ -2265,7 +2768,7 @@ nextinst:
sprintf(tempname, "_noconnect_%d_", localcount++);
Node(tempname);
join(tempname, nobj->name);
Fprintf(stderr, "Note: Implicit pin %s in instance "
Fprintf(stdout, "Note: Implicit pin %s in instance "
"%s of %s in cell %s\n",
scan->name, sobj->instance.name,
modulename, CurrentCell->name);
+1
View File
@@ -17,6 +17,7 @@ along with this program; see the file copying. If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* inetcomp.c -- a simple wrapper to the NETCOMP() function */
#include "config.h"
#include <stdio.h>
#include "netgen.h"
+1
View File
@@ -17,6 +17,7 @@ along with this program; see the file copying. If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* netcomp.c -- a simple wrapper to provide netlist comparison functionality */
#include "config.h"
#include <stdio.h>
#include "netgen.h"
+1
View File
@@ -23,6 +23,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdlib.h> /* for getenv */
#endif
#include "netgen.h"
#include "print.h"
int main(int argc, char **argv)
{
+2
View File
@@ -20,9 +20,11 @@ along with this program; see the file copying. If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ntk2xnf.c -- a simple wrapper to translate .ntk to Xilinx XNF format */
#include "config.h"
#include <stdio.h>
#include "netgen.h"
#include "xilinx.h"
#ifdef HAVE_X11
/* the following two X procedures are to permit linking
+699 -418
View File
File diff suppressed because it is too large Load Diff
+1487 -1430
View File
File diff suppressed because it is too large Load Diff
+21 -55
View File
@@ -668,7 +668,6 @@ XMKMF
HAVE_PYTHON3
EGREP
GREP
M4
RANLIB
INSTALL_DATA
INSTALL_SCRIPT
@@ -712,6 +711,7 @@ infodir
docdir
oldincludedir
includedir
runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -794,6 +794,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1046,6 +1047,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1183,7 +1193,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir
libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1336,6 +1346,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -3690,56 +3701,6 @@ else
fi
for ac_prog in gm4 gnum4 m4
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_M4+:} false; then :
$as_echo_n "(cached) " >&6
else
case $M4 in
[\\/]* | ?:[\\/]*)
ac_cv_path_M4="$M4" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_M4="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
M4=$ac_cv_path_M4
if test -n "$M4"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $M4" >&5
$as_echo "$M4" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$M4" && break
done
test -n "$M4" || M4="no"
if test x$M4 = xno; then
as_fn_error $? "M4 is required" "$LINENO" 5
fi
if test "$CPP" = "$CC -E" ; then
CPP="$CPP -x c"
@@ -6065,7 +6026,6 @@ fi
if test $usingTcl ; then
usingX11=1
cadinstall="$cadinstall tcltk"
modules="$modules tcltk"
programs="$programs tcltk"
@@ -6075,7 +6035,11 @@ if test $usingTcl ; then
extra_libs="$extra_libs \${NETGENDIR}/tcltk/libtcltk.o"
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
if test $usingX11 ; then
stub_defs="$stub_defs -DUSE_TCL_STUBS"
else
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
fi
else
programs="$programs netgen"
unused="$unused tcltk"
@@ -6160,6 +6124,7 @@ if test $usingTcl ; then
# Tk libraries and header files
#
# -----------------------------------------------------------------------
if test $usingX11 ; then
if test "${TK_INC_DIR}" != "/usr/include" ; then
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
fi
@@ -6176,6 +6141,7 @@ if test $usingTcl ; then
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
fi
fi
fi
# -----------------------------------------------------------------------
#
@@ -6285,7 +6251,7 @@ if test $usingTcl ; then
*darwin*)
SHDLIB_EXT=".dylib"
LDDL_FLAGS="-dynamiclib -flat_namespace -undefined suppress -noprebind"
LDDL_FLAGS="-dynamiclib -undefined dynamic_lookup"
LDFLAGS="${LDFLAGS} ${LIB_SPECS}"
CFLAGS="${CFLAGS} ${X_CFLAGS} ${INC_SPECS} -I/sw/include -fno-common"
;;
+8 -9
View File
@@ -26,12 +26,6 @@ AC_ISC_POSIX
AC_PROG_INSTALL
AC_PROG_RANLIB
dnl GNU M4 is preferred due to some of the option switches.
AC_PATH_PROGS([M4], [gm4 gnum4 m4], [no])
if test x$M4 = xno; then
AC_MSG_ERROR([M4 is required])
fi
dnl check size of pointer for correct behavior on 64-bit systems
dnl If the C preprocessor is GCC, we need to force the flag to
dnl assert that input files are of type C, or else the preprocessing
@@ -807,7 +801,6 @@ dnl "make" instead of requiring "make tcl"
dnl ----------------------------------------------------------------
if test $usingTcl ; then
usingX11=1
cadinstall="$cadinstall tcltk"
modules="$modules tcltk"
programs="$programs tcltk"
@@ -816,7 +809,11 @@ if test $usingTcl ; then
AC_DEFINE(TCL_NETGEN)
extra_libs="$extra_libs \${NETGENDIR}/tcltk/libtcltk.o"
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
if test $usingX11 ; then
stub_defs="$stub_defs -DUSE_TCL_STUBS"
else
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
fi
else
programs="$programs netgen"
unused="$unused tcltk"
@@ -903,6 +900,7 @@ if test $usingTcl ; then
# Tk libraries and header files
#
# -----------------------------------------------------------------------
if test $usingX11 ; then
if test "${TK_INC_DIR}" != "/usr/include" ; then
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
fi
@@ -919,6 +917,7 @@ if test $usingTcl ; then
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
fi
fi
fi
# -----------------------------------------------------------------------
#
@@ -1028,7 +1027,7 @@ if test $usingTcl ; then
*darwin*)
SHDLIB_EXT=".dylib"
LDDL_FLAGS="-dynamiclib -flat_namespace -undefined suppress -noprebind"
LDDL_FLAGS="-dynamiclib -undefined dynamic_lookup"
LDFLAGS="${LDFLAGS} ${LIB_SPECS}"
CFLAGS="${CFLAGS} ${X_CFLAGS} ${INC_SPECS} -I/sw/include -fno-common"
;;
File diff suppressed because it is too large Load Diff
+31 -4
View File
@@ -479,8 +479,12 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
netgen::compare assign "$fnum1 $cell1" "$fnum2 $cell2"
if {[file exists $setupfile]} {
puts stdout "Reading setup file $setupfile"
if {$setupfile == ""} {
puts stdout "\nNo setup file specified. Using trivial default setup.\n"
netgen::permute default ;# transistors and resistors
netgen::property default
} elseif {[file exists $setupfile]} {
puts stdout "\nReading setup file $setupfile\n"
# Instead of sourcing the setup file, run each line so we can
# catch individual errors and not let them halt the LVS process
set perrors 0
@@ -508,8 +512,10 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
puts stdout "Warning: There were errors reading the setup file"
}
} elseif {[string first nosetup $setupfile] < 0} {
netgen::permute default ;# transistors and resistors
netgen::property default
puts stderr "\nError: Setup file $setupfile does not exist.\n"
return
} else {
puts stderr "\nNo setup file specified. Continuing without a setup.\n"
}
if {[string first nolog $logfile] < 0} {
@@ -588,6 +594,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
lappend properr [lindex $endval 0]
} elseif {$uresult == -2} { ;# unmatched pins
set doCheckFlatten 1
} elseif {$uresult == -4} { ;# unmatched pins and properties
lappend properr [lindex $endval 0]
set doCheckFlatten 1
}
} else {
# not equivalent
@@ -649,6 +658,11 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
}
} elseif {[netgen::print queue] == {} && $result == 0} {
set pinMismatch 1
} else {
# This assumes that proxy pins are added correctly. Previously,
# that was not trusted, and so an initial pin mismatch would
# always force subcells to be flattened.
set doFlatten 0
}
}
if {$doFlatten} {
@@ -735,6 +749,19 @@ set auto_noexec 1 ;# don't EVER call UNIX commands w/o "shell" in front
# Cross-Application section
#----------------------------------------------------------------------
# For use with open_pdks, set PDK_ROOT from the environment. If no
# such environment variable exists, check some common locations.
if {[catch {set PDK_ROOT $::env(PDK_ROOT)}]} {
if {[file isdir /usr/local/share/pdk] == 1} {
set PDK_ROOT /usr/local/share/pdk
} elseif {[file isdir /usr/share/pdk] == 1} {
set PDK_ROOT /usr/share/pdk
} elseif {[file isdir /foss/pdk] == 1} {
set PDK_ROOT /foss/pdk
}
}
# Setup IRSIM assuming that the Tcl version is installed.
# We do not need to rename procedure irsim to NULL because it is
# redefined in a script, which simply overwrites the original.
+159 -33
View File
@@ -707,11 +707,11 @@ _netgen_readnet(ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *formats[] = {
"automatic", "ext", "extflat", "sim", "ntk", "spice",
"automatic", "ext", "extflat", "sim", "prm", "ntk", "spice",
"verilog", "netgen", "actel", "xilinx", NULL
};
enum FormatIdx {
AUTO_IDX, EXT_IDX, EXTFLAT_IDX, SIM_IDX, NTK_IDX,
AUTO_IDX, EXT_IDX, EXTFLAT_IDX, SIM_IDX, PRM_IDX, NTK_IDX,
SPICE_IDX, VERILOG_IDX, NETGEN_IDX, ACTEL_IDX, XILINX_IDX
};
struct nlist *tc;
@@ -807,6 +807,9 @@ _netgen_readnet(ClientData clientData,
case SIM_IDX:
retstr = ReadSim(savstr, &filenum);
break;
case PRM_IDX:
retstr = ReadPrm(savstr, &filenum);
break;
case NTK_IDX:
retstr = ReadNtk(savstr, &filenum);
break;
@@ -1592,8 +1595,7 @@ _netgen_model(ClientData clientData,
return result;
if (objc == 3) {
model = Tcl_GetString(objv[2]);
nports = NumberOfPorts(model, fnum);
nports = NumberOfPorts(tp->name, fnum);
if (Tcl_GetIndexFromObj(interp, objv[2], (CONST84 char **)modelclasses,
"class", 0, &index) != TCL_OK) {
@@ -2228,7 +2230,13 @@ _netcmp_compare(ClientData clientData,
Fprintf(stdout, "\nCircuit 1 cell %s is a black box; will not flatten "
"Circuit 2\n", name1);
}
else if (hascontents1 || hascontents2) {
else if (!hascontents1 && !hascontents2 && (tp1->flags & CELL_PLACEHOLDER)
&& (tp2->flags & CELL_PLACEHOLDER)) {
/* Two empty subcircuits, don't flatten anything */
Fprintf(stdout, "\nCircuit 1 cell %s and Circuit 2 cell %s are black"
" boxes.\n", name1, name2);
}
else {
FlattenUnmatched(tp1, name1, 1, 0);
FlattenUnmatched(tp2, name2, 1, 0);
DescribeContents(name1, fnum1, name2, fnum2);
@@ -2241,10 +2249,6 @@ _netcmp_compare(ClientData clientData,
DescribeContents(name1, fnum1, name2, fnum2);
}
}
else { /* Two empty subcircuits */
Fprintf(stdout, "\nCircuit 1 cell %s and Circuit 2 cell %s are black"
" boxes.\n", name1, name2);
}
CreateTwoLists(name1, fnum1, name2, fnum2, dolist);
// Return the names of the two cells being compared, if doing "compare
@@ -2508,7 +2512,13 @@ _netcmp_run(ClientData clientData,
if (automorphisms > 0) {
// Next, attempt to resolve automorphisms uniquely by
// using the pin names
automorphisms = ResolveAutomorphsByPin();
automorphisms = ResolveAutomorphsByPin(FALSE);
}
if (automorphisms > 0) {
// Next, attempt to resolve automorphisms uniquely by
// using the net names (should only be done after
// resolving by pin).
automorphisms = ResolveAutomorphsByPin(TRUE);
}
if (automorphisms > 0) {
// Anything left is truly indistinguishable
@@ -2518,8 +2528,16 @@ _netcmp_run(ClientData clientData,
if (automorphisms == -1)
Fprintf(stdout, "Netlists do not match.\n");
else if (automorphisms == -2)
Fprintf(stdout, "Netlists match uniquely with port errors.\n");
else if (automorphisms == -2) {
Fprintf(stdout, "Netlists match uniquely with port");
if (PropertyErrorDetected) {
Fprintf(stdout, " and property errors.\n");
PrintPropertyResults(dolist);
}
else {
Fprintf(stdout, " errors.\n");
}
}
else {
if (automorphisms == 0)
Fprintf(stdout, "Netlists match uniquely");
@@ -2554,6 +2572,7 @@ _netcmp_run(ClientData clientData,
/* 0: not verified */
/* -1: no elements or nodes */
/* -3: verified with property error */
/* -4: verified with property and port errors */
/* equiv option */
/* -2: pin mismatch */
/* */
@@ -2647,14 +2666,20 @@ _netcmp_verify(ClientData clientData,
disable_interrupt();
if (index == EQUIV_IDX || index == UNIQUE_IDX)
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
else
else {
Fprintf(stdout, "Netlists do not match.\n");
Fprintf(stdout, "Port matching may fail to disambiguate symmetries.\n");
}
}
else if (automorphisms == -2) {
if (index == EQUIV_IDX)
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
else if (index == UNIQUE_IDX)
Tcl_SetObjResult(interp, Tcl_NewIntObj(-2));
else if (index == UNIQUE_IDX) {
if (PropertyErrorDetected == 0)
Tcl_SetObjResult(interp, Tcl_NewIntObj(-2));
else
Tcl_SetObjResult(interp, Tcl_NewIntObj(-4));
}
else if (index > 0)
Fprintf(stdout, "Circuits match uniquely with port errors.\n");
}
@@ -2900,7 +2925,7 @@ _netcmp_equate(ClientData clientData,
struct ElementClass *saveEclass = NULL;
struct NodeClass *saveNclass = NULL;
int file1, file2;
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0, dounique = 0;
Tcl_Obj *tobj1, *tobj2, *tobj3;
while (objc > 1) {
@@ -2916,6 +2941,11 @@ _netcmp_equate(ClientData clientData,
objv++;
objc--;
}
else if (!strcmp(optstart, "unique")) {
dounique = 1;
objv++;
objc--;
}
else
break;
}
@@ -3340,19 +3370,9 @@ _netcmp_equate(ClientData clientData,
return TCL_ERROR;
}
}
/* Now that all pins are assigned by name, reorder */
/* the pin lists of the 2nd cell to match the */
/* order of the 1st. */
/* Reorder the pin lists of instances of the 2nd */
/* cell to match the order of the 1st. */
// pindata.cell2 = tp2;
// RecurseCellHashTable2(pinorder, (void *)(&pindata));
}
if (EquivalenceClasses(tp1->name, file1, tp2->name, file2)) {
if (EquivalenceClasses(tp1->name, file1, tp2->name, file2, dounique)) {
Fprintf(stdout, "Device classes %s and %s are equivalent.\n",
tp1->name, tp2->name);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1));
@@ -3375,6 +3395,8 @@ _netcmp_equate(ClientData clientData,
/* add --- add new property */
/* remove --- delete existing property */
/* tolerance --- set property tolerance */
/* associate --- associate property with a pin */
/* topology --- set exact/relaxed matching */
/* merge --- (deprecated) */
/* or */
/* netgen::property default */
@@ -3411,13 +3433,21 @@ _netcmp_property(ClientData clientData,
double dval;
int ival, argstart;
char *topoptions[] = {
"default", "series", "serial", "parallel", "topology", NULL
};
enum TopOptionIdx {
TOP_DEFAULT_IDX, TOP_SERIES_IDX, TOP_SERIAL_IDX, TOP_PARALLEL_IDX,
TOP_TOPOLOGY_IDX
};
char *options[] = {
"add", "create", "remove", "delete", "tolerance", "merge", "serial",
"series", "parallel", NULL
"series", "parallel", "associate", "derive", NULL
};
enum OptionIdx {
ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX,
SERIAL_IDX, SERIES_IDX, PARALLEL_IDX
SERIAL_IDX, SERIES_IDX, PARALLEL_IDX, ASSOCIATE_IDX, DERIVE_IDX
};
int result, index, idx2;
@@ -3452,6 +3482,14 @@ _netcmp_property(ClientData clientData,
COMB_NONE_IDX, COMB_PAR_IDX, COMB_ADD_IDX, COMB_CRITICAL_IDX
};
char *deriveoptions[] = {
"area", "perimeter", NULL
};
enum DeriveOptionIdx {
AREA_IDX, PERIMETER_IDX
};
char *yesno[] = {
"on", "yes", "true", "enable", "allow",
"off", "no", "false", "disable", "prohibit", NULL
@@ -3462,8 +3500,17 @@ _netcmp_property(ClientData clientData,
return TCL_ERROR;
}
char *topo[] = {
"strict", "relaxed", NULL
};
/* Don't need to check return value */
index = -1;
Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)topoptions,
"option", 0, &index);
/* Check for special command "property default" */
if ((objc == 2) && (!strcmp(Tcl_GetString(objv[1]), "default"))) {
if ((objc == 2) && (index == TOP_DEFAULT_IDX)) {
/* For each FET device, do "merge {w add_critical}" and */
/* "remove as ad ps pd". This allows parallel devices */
@@ -3510,7 +3557,7 @@ _netcmp_property(ClientData clientData,
}
return TCL_OK;
}
else if ((objc == 3) && (!strcmp(Tcl_GetString(objv[1]), "parallel"))) {
else if ((objc == 3) && (index == TOP_PARALLEL_IDX)) {
if (!strcmp(Tcl_GetString(objv[2]), "none")) {
GlobalParallelNone = TRUE;
SetParallelCombine(FALSE);
@@ -3532,8 +3579,7 @@ _netcmp_property(ClientData clientData,
}
return TCL_OK;
}
else if ((objc == 3) && ((!strcmp(Tcl_GetString(objv[1]), "series")) ||
(!strcmp(Tcl_GetString(objv[1]), "serial")))) {
else if ((objc == 3) && ((index == TOP_SERIES_IDX) || (index == TOP_SERIAL_IDX))) {
if (!strcmp(Tcl_GetString(objv[2]), "none")) {
SetSeriesCombine(FALSE);
}
@@ -3547,6 +3593,36 @@ _netcmp_property(ClientData clientData,
}
return TCL_OK;
}
else if ((objc > 1) && (index == TOP_TOPOLOGY_IDX)) {
if (objc == 2) {
if (ExactTopology)
Tcl_SetResult(interp, "Strict topology property matching.",
NULL);
else
Tcl_SetResult(interp, "Relaxed topology property matching.",
NULL);
}
else if (objc == 3) {
if (Tcl_GetIndexFromObj(interp, objv[2],
(CONST84 char **)topo,
"topology", 0, &idx2) == TCL_OK) {
if (idx2 == 0)
ExactTopology = TRUE;
else if (idx2 == 1)
ExactTopology = FALSE;
else {
Tcl_SetResult(interp, "Topology matching type must be "
"'strict' or 'relaxed'.", NULL);
return TCL_ERROR;
}
}
}
else {
Tcl_WrongNumArgs(interp, 1, objv, "strict|relaxed");
return TCL_ERROR;
}
return TCL_OK;
}
result = CommonParseCell(interp, objv[1], &tp, &fnum);
if (result != TCL_OK) return result;
@@ -3840,6 +3916,29 @@ _netcmp_property(ClientData clientData,
}
break;
case ASSOCIATE_IDX:
if (objc == 3) {
Tcl_WrongNumArgs(interp, 1, objv, "{property_key pin_name} ...");
return TCL_ERROR;
}
for (i = 3; i < objc; i++) {
// Each value must be a duplet
result = Tcl_ListObjLength(interp, objv[i], &llen);
if ((result != TCL_OK) || (llen != 2)) {
Tcl_SetResult(interp, "Not a {key pin} pair list.",
NULL);
}
else {
result = Tcl_ListObjIndex(interp, objv[i], 0, &tobj1);
if (result != TCL_OK) return result;
result = Tcl_ListObjIndex(interp, objv[i], 1, &tobj2);
if (result != TCL_OK) return result;
PropertyAssociatePin(tp->name, fnum, Tcl_GetString(tobj1),
Tcl_GetString(tobj2));
}
}
break;
case TOLERANCE_IDX:
if (objc == 3) {
Tcl_WrongNumArgs(interp, 1, objv, "{property_key tolerance} ...");
@@ -3939,6 +4038,33 @@ _netcmp_property(ClientData clientData,
}
}
break;
case DERIVE_IDX:
/* Create a derived property. For now, this just creates
* "area" or "perimeter" properties. There may not (?)
* be enough cause to make other ones. Unlike other
* "property" options, this option causes the whole circuit
* database to be searched for the device in question, and
* the new property is added.
*/
if (objc < 6) {
Tcl_WrongNumArgs(interp, 2, objv, "{area|perimeter width length}");
return TCL_ERROR;
}
result = Tcl_GetIndexFromObj(interp, objv[3],
(CONST84 char **)deriveoptions,
"area|perimeter", 0, &idx2);
if (result != TCL_OK) return result;
switch (idx2) {
case AREA_IDX:
DeriveAreaProperty(tp, fnum, Tcl_GetString(objv[4]),
Tcl_GetString(objv[5]));
break;
case PERIMETER_IDX:
DerivePerimeterProperty(tp, fnum, Tcl_GetString(objv[4]),
Tcl_GetString(objv[5]));
break;
}
}
}
return TCL_OK;
+123 -80
View File
@@ -36,7 +36,7 @@ exec ${NETGEN_WISH:=wish} "$0" ${1+"$@"}
#
# set ::tkcon::PRIV(proxy) {wwwproxy:8080 1}
#
# Or you can set the above variable from within tkcon by calling
# Or you can set the above variable from within tkcon by calling
#
# tkcon master set ::tkcon:PRIV(proxy) wwwproxy:8080
#
@@ -44,6 +44,8 @@ exec ${NETGEN_WISH:=wish} "$0" ${1+"$@"}
if {$tcl_version < 8.0} {
return -code error "tkcon requires at least Tcl/Tk8"
} else {
# Prevent breaking on version 8.5.2
# package require -exact Tk $tcl_version
package require Tk $tcl_version
}
@@ -59,18 +61,6 @@ foreach pkg [info loaded {}] {
}
catch {unset pkg file name version}
# Tk 8.4 makes previously exposed stuff private.
# FIX: Update tkcon to not rely on the private Tk code.
#
if {![llength [info globals tkPriv]]} {
::tk::unsupported::ExposePrivateVariable tkPriv
}
foreach cmd {SetCursor UpDownLine Transpose ScrollPages} {
if {![llength [info commands tkText$cmd]]} {
::tk::unsupported::ExposePrivateCommand tkText$cmd
}
}
# Initialize the ::tkcon namespace
#
namespace eval ::tkcon {
@@ -196,7 +186,7 @@ proc ::tkcon::Init {} {
tkcon_puts tkcon_gets observe observe_var unalias which what
}
version 2.3
RCS {RCS: @(#) $Id: tkcon.tcl,v 1.2 2008/05/23 00:20:17 tim Exp $}
RCS {RCS: @(#) $Id: tkcon.tcl,v 1.2 2008/04/18 16:28:13 tim Exp $}
HEADURL {http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tkcon/tkcon/tkcon.tcl?rev=HEAD}
docs "http://tkcon.sourceforge.net/"
email {jeff@hobbs.org}
@@ -654,7 +644,7 @@ proc ::tkcon::GarbageCollect {} {
## ::tkcon::EvalCmd) in turn. Any uncompleted command will not be eval'ed.
# ARGS: w - console text widget
# Calls: ::tkcon::CmdGet, ::tkcon::CmdSep, ::tkcon::EvalCmd
##
##
proc ::tkcon::Eval {w} {
set incomplete [CmdSep [CmdGet $w] cmds last]
$w mark set insert end-1c
@@ -674,7 +664,7 @@ proc ::tkcon::Eval {w} {
# Calls: ::tkcon::Prompt
# Outputs: result of command to stdout (or stderr if error occured)
# Returns: next event number
##
##
proc ::tkcon::EvalCmd {w cmd} {
variable OPT
variable PRIV
@@ -745,7 +735,7 @@ proc ::tkcon::EvalCmd {w cmd} {
$w tag bind $tag <Leave> \
[list $w tag configure $tag -underline 0]
$w tag bind $tag <ButtonRelease-1> \
"if {!\[info exists tkPriv(mouseMoved)\] || !\$tkPriv(mouseMoved)} \
"if {!\[info exists ::tk::Priv(mouseMoved)\] || !\$::tk::Priv(mouseMoved)} \
{[list edit -attach [Attach] -type error -- $PRIV(errorInfo)]}"
} else {
$w insert output $res\n stderr
@@ -905,7 +895,7 @@ proc ::tkcon::EvalSocketClosed {} {
## ::tkcon::EvalNamespace - evaluates the args in a particular namespace
## This is an override for ::tkcon::EvalAttached for when the user wants
## to attach to a particular namespace of the attached interp
# ARGS: attached
# ARGS: attached
# namespace the namespace to evaluate in
# args the args to evaluate
# RETURNS: the result of the command
@@ -933,7 +923,7 @@ proc ::tkcon::Namespaces {{ns ::} {l {}}} {
## ::tkcon::CmdGet - gets the current command from the console widget
# ARGS: w - console text widget
# Returns: text which compromises current command line
##
##
proc ::tkcon::CmdGet w {
if {![llength [$w tag nextrange prompt limit end]]} {
$w tag add stdin limit end-1c
@@ -947,7 +937,7 @@ proc ::tkcon::CmdGet w {
# last - varname of any remainder (like an incomplete final command).
# If there is only one command, it's placed in this var.
# Returns: constituent command info in varnames specified by list & rmd.
##
##
proc ::tkcon::CmdSep {cmd list last} {
upvar 1 $list cmds $last inc
set inc {}
@@ -974,7 +964,7 @@ proc ::tkcon::CmdSep {cmd list last} {
## ::tkcon::CmdSplit - splits multiple commands into a list
# ARGS: cmd - (possible) multiple command to separate
# Returns: constituent commands in a list
##
##
proc ::tkcon::CmdSplit {cmd} {
set inc {}
set cmds {}
@@ -998,7 +988,7 @@ proc ::tkcon::CmdSplit {cmd} {
## Called by ::tkcon::EvalCmd
# ARGS: w - text widget
# Outputs: tag name guaranteed unique in the widget
##
##
proc ::tkcon::UniqueTag {w} {
set tags [$w tag names]
set idx 0
@@ -1011,7 +1001,7 @@ proc ::tkcon::UniqueTag {w} {
# ARGS: w - console text widget
# size - # of lines to constrain to
# Outputs: may delete data in console widget
##
##
proc ::tkcon::ConstrainBuffer {w size} {
if {[$w index end] > $size} {
$w delete 1.0 [expr {int([$w index end])-$size}].0
@@ -1021,7 +1011,7 @@ proc ::tkcon::ConstrainBuffer {w size} {
## ::tkcon::Prompt - displays the prompt in the console widget
# ARGS: w - console text widget
# Outputs: prompt (specified in ::tkcon::OPT(prompt1)) to console
##
##
proc ::tkcon::Prompt {{pre {}} {post {}} {prompt {}}} {
variable OPT
variable PRIV
@@ -1053,7 +1043,7 @@ proc ::tkcon::Prompt {{pre {}} {post {}} {prompt {}}} {
}
## ::tkcon::About - gives about info for tkcon
##
##
proc ::tkcon::About {} {
variable OPT
variable PRIV
@@ -1088,7 +1078,7 @@ proc ::tkcon::About {} {
## ::tkcon::InitMenus - inits the menubar and popup for the console
# ARGS: w - console text widget
##
##
proc ::tkcon::InitMenus {w title} {
variable OPT
variable PRIV
@@ -1521,7 +1511,7 @@ proc ::tkcon::NamespaceMenu m {
}
}
## Namepaces List
## Namepaces List
##
proc ::tkcon::NamespacesList {names} {
variable PRIV
@@ -1894,7 +1884,7 @@ proc ::tkcon::NewSocket {} {
## The file is actually sourced in the currently attached's interp
# ARGS: fn - (optional) filename to source in
# Returns: selected filename ({} if nothing was selected)
##
##
proc ::tkcon::Load { {fn ""} } {
set types {
{{Tcl Files} {.tcl .tk}}
@@ -1913,7 +1903,7 @@ proc ::tkcon::Load { {fn ""} } {
## This does not eval in a slave because it's not necessary
# ARGS: w - console text widget
# fn - (optional) filename to save to
##
##
proc ::tkcon::Save { {fn ""} {type ""} {opt ""} {mode w} } {
variable PRIV
@@ -1983,7 +1973,7 @@ proc ::tkcon::MainInit {} {
## Creates a slave interpreter and sources in this script.
## All other interpreters also get a command to eval function in the
## new interpreter.
##
##
proc ::tkcon::New {} {
variable PRIV
global argv0 argc argv
@@ -2020,7 +2010,7 @@ proc ::tkcon::MainInit {} {
## ::tkcon::Exit - full exit OR destroy slave console
## This proc should only be called in the main interpreter from a slave.
## The master determines whether we do a full exit or just kill the slave.
##
##
proc ::tkcon::Exit {slave args} {
variable PRIV
variable OPT
@@ -2043,7 +2033,7 @@ proc ::tkcon::MainInit {} {
## This proc should only be called by the main interpreter. If it is
## called from there, it will ask before exiting tkcon. All others
## (slaves) will just have their slave interpreter deleted, closing them.
##
##
proc ::tkcon::Destroy {{slave {}}} {
variable PRIV
@@ -2474,7 +2464,7 @@ proc ::tkcon::ErrorHighlight w {
$w tag configure $tag -foreground $COLOR(stdout)
$w tag bind $tag <Enter> [list $w tag configure $tag -underline 1]
$w tag bind $tag <Leave> [list $w tag configure $tag -underline 0]
$w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
$w tag bind $tag <ButtonRelease-1> "if {!\$::tk::Priv(mouseMoved)} \
{[list edit -attach $app -type proc -find $what -- $cmd]}"
}
set info [string range $info $c1 end]
@@ -2503,7 +2493,7 @@ proc ::tkcon::ErrorHighlight w {
$w tag configure $tag -foreground $COLOR(proc)
$w tag bind $tag <Enter> [list $w tag configure $tag -underline 1]
$w tag bind $tag <Leave> [list $w tag configure $tag -underline 0]
$w tag bind $tag <ButtonRelease-1> "if {!\$tkPriv(mouseMoved)} \
$w tag bind $tag <ButtonRelease-1> "if {!\$::tk::Priv(mouseMoved)} \
{[list edit -attach $app -type proc -- $cmd]}"
}
}
@@ -2513,7 +2503,7 @@ proc ::tkcon::ErrorHighlight w {
## This always exists in the main interpreter, and is aliased into
## other connected interpreters
# ARGS: totally variable, see internal comments
##
##
proc tkcon {cmd args} {
global errorInfo
@@ -2552,8 +2542,8 @@ proc tkcon {cmd args} {
## 'congets' a replacement for [gets stdin]
# Use the 'gets' alias of 'tkcon_gets' command instead of
# calling the *get* methods directly for best compatability
if {[llength $args]} {
return -code error "wrong # args: must be \"tkcon congets\""
if {[llength $args] > 1} {
return -code error "wrong # args: must be \"tkcon congets [pfix]\""
}
tkcon show
set old [bind TkConsole <<TkCon_Eval>>]
@@ -2561,7 +2551,12 @@ proc tkcon {cmd args} {
set w $::tkcon::PRIV(console)
# Make sure to move the limit to get the right data
$w mark set insert end
$w mark set limit insert
if {[llength $args]} {
$w mark set limit insert
$w insert end $args
} else {
$w mark set limit insert
}
$w see end
vwait ::tkcon::PRIV(wait)
set line [::tkcon::CmdGet $w]
@@ -2790,21 +2785,27 @@ proc tkcon {cmd args} {
## This allows me to capture all stdout/stderr to the console window
## This will be renamed to 'puts' at the appropriate time during init
##
# ARGS: same as usual
# ARGS: same as usual
# Outputs: the string with a color-coded text tag
##
##
proc tkcon_puts args {
set len [llength $args]
foreach {arg1 arg2 arg3} $args { break }
if {$len == 1} {
tkcon console insert output "$arg1\n" stdout
set sarg $arg1
set nl 1
set farg stdout
} elseif {$len == 2} {
if {![string compare $arg1 -nonewline]} {
tkcon console insert output $arg2 stdout
set sarg $arg2
set farg stdout
set nl 0
} elseif {![string compare $arg1 stdout] \
|| ![string compare $arg1 stderr]} {
tkcon console insert output "$arg2\n" $arg1
set sarg $arg2
set farg $arg1
set nl 1
} else {
set len 0
}
@@ -2812,11 +2813,15 @@ proc tkcon_puts args {
if {![string compare $arg1 -nonewline] \
&& (![string compare $arg2 stdout] \
|| ![string compare $arg2 stderr])} {
tkcon console insert output $arg3 $arg2
set sarg $arg3
set farg $arg2
set nl 0
} elseif {(![string compare $arg1 stdout] \
|| ![string compare $arg1 stderr]) \
&& ![string compare $arg3 nonewline]} {
tkcon console insert output $arg2 $arg1
set sarg $arg2
set farg $arg1
set nl 0
} else {
set len 0
}
@@ -2826,7 +2831,42 @@ proc tkcon_puts args {
## $len == 0 means it wasn't handled by tkcon above.
##
if {$len == 0} {
if {$len != 0} {
## "poor man's" \r substitution---erase everything on the output
## line and print from character after the \r
set rpt [string last \r $sarg]
if {$rpt >= 0} {
tkcon console delete "insert linestart" "insert lineend"
set sarg [string range $sarg [expr {$rpt + 1}] end]
}
set bpt [string first \b $sarg]
if {$bpt >= 0} {
set narg [string range $sarg [expr {$bpt + 1}] end]
set sarg [string range $sarg 0 [expr {$bpt - 1}]]
set nl 0
}
if {$nl == 0} {
tkcon console insert output $sarg $farg
} else {
tkcon console insert output "$sarg\n" $farg
}
if {$bpt >= 0} {
tkcon console delete "insert -1 char" insert
if {$nl == 0} {
tkcon_puts $farg $narg nonewline
} else {
tkcon_puts $farg $narg
}
}
} else {
global errorCode errorInfo
if {[catch "tkcon_tcl_puts $args" msg]} {
regsub tkcon_tcl_puts $msg puts msg
@@ -2849,7 +2889,7 @@ proc tkcon_puts args {
## This allows me to capture all stdin input without needing to stdin
## This will be renamed to 'gets' at the appropriate time during init
##
# ARGS: same as gets
# ARGS: same as gets
# Outputs: same as gets
##
proc tkcon_gets args {
@@ -2873,12 +2913,12 @@ proc tkcon_gets args {
}
## edit - opens a file/proc/var for reading/editing
##
##
# Arguments:
# type proc/file/var
# what the actual name of the item
# Returns: nothing
##
##
proc edit {args} {
array set opts {-find {} -type {} -attach {}}
while {[string match -* [lindex $args 0]]} {
@@ -3030,7 +3070,7 @@ proc echo args { puts [concat $args] }
## clear - clears the buffer of the console (not the history though)
## This is executed in the parent interpreter
##
##
proc clear {{pcnt 100}} {
if {![regexp {^[0-9]*$} $pcnt] || $pcnt < 1 || $pcnt > 100} {
return -code error \
@@ -3048,7 +3088,7 @@ proc clear {{pcnt 100}} {
## If called with one arg, returns the alias of that arg (or {} if none)
# ARGS: newcmd - (optional) command to bind alias to
# args - command and args being aliased
##
##
proc alias {{newcmd {}} args} {
if {[string match {} $newcmd]} {
set res {}
@@ -3065,7 +3105,7 @@ proc alias {{newcmd {}} args} {
## unalias - unaliases an alias'ed command
# ARGS: cmd - command to unbind as an alias
##
##
proc unalias {cmd} {
interp alias {} $cmd {}
}
@@ -3085,7 +3125,7 @@ proc unalias {cmd} {
# -- forcibly ends options recognition
#
# Returns: the values of the requested items in a 'source'able form
##
##
proc dump {type args} {
set whine 1
set code ok
@@ -3637,13 +3677,13 @@ proc observe_var {name el op} {
## which - tells you where a command is found
# ARGS: cmd - command name
# Returns: where command is found (internal / external / unknown)
##
##
proc which cmd {
## This tries to auto-load a command if not recognized
set types [uplevel 1 [list what $cmd 1]]
if {[llength $types]} {
set out {}
foreach type $types {
switch -- $type {
alias { set res "$cmd: aliased to [alias $cmd]" }
@@ -3674,7 +3714,7 @@ proc which cmd {
## what - tells you what a string is recognized as
# ARGS: str - string to id
# Returns: id types of command as list
##
##
proc what {str {autoload 0}} {
set types {}
if {[llength [info commands $str]] || ($autoload && \
@@ -3721,7 +3761,7 @@ proc what {str {autoload 0}} {
# -long - list in full format "permissions size date filename"
# -full - displays / after directories and link paths for links
# Returns: a directory listing
##
##
proc dir {args} {
array set s {
all 0 full 0 long 0
@@ -4106,7 +4146,7 @@ proc ::tkcon::Bindings {} {
global tcl_platform tk_version
#-----------------------------------------------------------------------
# Elements of tkPriv that are used in this file:
# Elements of ::tk::Priv that are used in this file:
#
# char - Character position on the line; kept in order
# to allow moving up or down past short lines while
@@ -4131,9 +4171,12 @@ proc ::tkcon::Bindings {} {
}
## Get all Text bindings into TkConsole
foreach ev [bind Text] { bind TkConsole $ev [bind Text $ev] }
foreach ev [bind Text] { bind TkConsole $ev [bind Text $ev] }
## We really didn't want the newline insertion
bind TkConsole <Control-Key-o> {}
## in 8.6b3, the virtual events <<NextLine>> and <<PrevLine>>
# mess up our history feature
bind TkConsole <<NextLine>> {}
bind TkConsole <<PrevLine>> {}
@@ -4342,9 +4385,9 @@ proc ::tkcon::Bindings {} {
bind TkConsole <Control-a> {
if {[%W compare {limit linestart} == {insert linestart}]} {
tkTextSetCursor %W limit
::tk::TextSetCursor %W limit
} else {
tkTextSetCursor %W {insert linestart}
::tk::TextSetCursor %W {insert linestart}
}
}
bind TkConsole <Key-Home> [bind TkConsole <Control-a>]
@@ -4368,14 +4411,14 @@ proc ::tkcon::Bindings {} {
}
bind TkConsole <<TkCon_Previous>> {
if {[%W compare {insert linestart} != {limit linestart}]} {
tkTextSetCursor %W [tkTextUpDownLine %W -1]
::tk::TextSetCursor %W [::tk::TextUpDownLine %W -1]
} else {
::tkcon::Event -1
}
}
bind TkConsole <<TkCon_Next>> {
if {[%W compare {insert linestart} != {end-1c linestart}]} {
tkTextSetCursor %W [tkTextUpDownLine %W 1]
::tk::TextSetCursor %W [::tk::TextUpDownLine %W 1]
} else {
::tkcon::Event 1
}
@@ -4390,7 +4433,7 @@ proc ::tkcon::Bindings {} {
}
bind TkConsole <<TkCon_Transpose>> {
## Transpose current and previous chars
if {[%W compare insert > "limit+1c"]} { tkTextTranspose %W }
if {[%W compare insert > "limit+1c"]} { ::tk::TextTranspose %W }
}
bind TkConsole <<TkCon_ClearLine>> {
## Clear command line (Unix shell staple)
@@ -4408,10 +4451,10 @@ proc ::tkcon::Bindings {} {
::tkcon::Insert %W $::tkcon::PRIV(tmp)
%W see end
}
catch {bind TkConsole <Key-Page_Up> { tkTextScrollPages %W -1 }}
catch {bind TkConsole <Key-Prior> { tkTextScrollPages %W -1 }}
catch {bind TkConsole <Key-Page_Down> { tkTextScrollPages %W 1 }}
catch {bind TkConsole <Key-Next> { tkTextScrollPages %W 1 }}
catch {bind TkConsole <Key-Page_Up> { ::tk::TextScrollPages %W -1 }}
catch {bind TkConsole <Key-Prior> { ::tk::TextScrollPages %W -1 }}
catch {bind TkConsole <Key-Page_Down> { ::tk::TextScrollPages %W 1 }}
catch {bind TkConsole <Key-Next> { ::tk::TextScrollPages %W 1 }}
bind TkConsole <$PRIV(meta)-d> {
if {[%W compare insert >= limit]} {
%W delete insert {insert wordend}
@@ -4429,7 +4472,7 @@ proc ::tkcon::Bindings {} {
}
bind TkConsole <ButtonRelease-2> {
if {
(!$tkPriv(mouseMoved) || $tk_strictMotif) &&
(!$::tk::Priv(mouseMoved) || $tk_strictMotif) &&
![catch {::tkcon::GetSelection %W} ::tkcon::PRIV(tmp)]
} {
if {[%W compare @%x,%y < limit]} {
@@ -4600,7 +4643,7 @@ proc ::tkcon::TagProc w {
# c1 - first char of pair
# c2 - second char of pair
# Calls: ::tkcon::Blink
##
##
proc ::tkcon::MatchPair {w c1 c2 {lim 1.0}} {
if {[string compare {} [set ix [$w search -back $c1 insert $lim]]]} {
while {
@@ -4638,7 +4681,7 @@ proc ::tkcon::MatchPair {w c1 c2 {lim 1.0}} {
## The quote to match is assumed to be at the text index 'insert'.
# ARGS: w - console text widget
# Calls: ::tkcon::Blink
##
##
proc ::tkcon::MatchQuote {w {lim 1.0}} {
set i insert-1c
set j 0
@@ -4664,7 +4707,7 @@ proc ::tkcon::MatchQuote {w {lim 1.0}} {
# i2 - end index of blink region
# dur - duration in usecs to blink for
# Outputs: blinks selected characters in $w
##
##
proc ::tkcon::Blink {w args} {
eval [list $w tag add blink] $args
after $::tkcon::OPT(blinktime) [list $w] tag remove blink $args
@@ -4679,7 +4722,7 @@ proc ::tkcon::Blink {w args} {
# ARGS: w - text window in which to insert the string
# s - string to insert (usually just a single char)
# Outputs: $s to text widget
##
##
proc ::tkcon::Insert {w s} {
if {[string match {} $s] || [string match disabled [$w cget -state]]} {
return
@@ -4695,7 +4738,7 @@ proc ::tkcon::Insert {w s} {
$w see insert
}
## ::tkcon::Expand -
## ::tkcon::Expand -
# ARGS: w - text widget in which to expand str
# type - type of expansion (path / proc / variable)
# Calls: ::tkcon::Expand(Pathname|Procname|Variable)
@@ -4704,7 +4747,7 @@ proc ::tkcon::Insert {w s} {
# match equaled the string to expand, then all possible matches
# are output to stdout. Triggers bell if no matches are found.
# Returns: number of matches found
##
##
proc ::tkcon::Expand {w {type ""}} {
set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"$\]"
set tmp [$w search -backwards -regexp $exp insert-1c limit-1c]
@@ -4743,7 +4786,7 @@ proc ::tkcon::Expand {w {type ""}} {
# Calls: ::tkcon::ExpandBestMatch
# Returns: list containing longest unique match followed by all the
# possible further matches
##
##
proc ::tkcon::ExpandPathname str {
set pwd [EvalAttached pwd]
# Cause a string like {C:/Program\ Files/} to become "C:/Program Files/"
@@ -4825,7 +4868,7 @@ proc ::tkcon::ExpandProcname str {
# Calls: ::tkcon::ExpandBestMatch
# Returns: list containing longest unique match followed by all the
# possible further matches
##
##
proc ::tkcon::ExpandVariable str {
if {[regexp {([^\(]*)\((.*)} $str junk ary str]} {
## Looks like they're trying to expand an array.
@@ -4853,7 +4896,7 @@ proc ::tkcon::ExpandVariable str {
## or $e is {}. $e is extra for compatibility with proc below.
# ARGS: l - list to find best unique match in
# Returns: longest unique match in the list
##
##
proc ::tkcon::ExpandBestMatch2 {l {e {}}} {
set s [lindex $l 0]
if {[llength $l]>1} {
@@ -4873,7 +4916,7 @@ proc ::tkcon::ExpandBestMatch2 {l {e {}}} {
# ARGS: l - list to find best unique match in
# e - currently best known unique match
# Returns: longest unique match in the list
##
##
proc ::tkcon::ExpandBestMatch {l {e {}}} {
set ec [lindex $l 0]
if {[llength $l]>1} {
@@ -5237,7 +5280,7 @@ proc ::tkcon::Retrieve {} {
## ::tkcon::Resource - re'source's this script into current console
## Meant primarily for my development of this program. It follows
## links until the ultimate source is found.
##
##
set ::tkcon::PRIV(SCRIPT) [info script]
if {!$::tkcon::PRIV(WWW) && [string compare $::tkcon::PRIV(SCRIPT) {}]} {
# we use a catch here because some wrap apps choke on 'file type'