Compare commits

...
41 Commits
Author SHA1 Message Date
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
13 changed files with 3202 additions and 1976 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.247
1.5.263
+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 {
+122 -20
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.
@@ -1177,7 +1214,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 +1224,12 @@ 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;
continue;
}
else {
@@ -1218,7 +1250,6 @@ int UniquePins(char *name, int filenum)
}
}
portcount++;
lob = ob;
}
if (needscleanup)
@@ -1600,6 +1631,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
ECompList *list0X, *listX0;
int hascontents1, hascontents2;
int match, modified = 0;
int not_top;
if (file1 == -1)
tc1 = LookupCell(name1);
@@ -1870,6 +1902,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 +1950,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 +1990,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;
@@ -2027,6 +2098,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 +2129,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;
+153 -52
View File
@@ -3773,20 +3773,23 @@ int CreateCompareQueue(char *name1, int file1, char *name2, int file2)
return 0;
}
/*----------------------------------------------*/
/* Read the top of the compare queue, but do */
/* not alter the stack. */
/*----------------------------------------------*/
/*----------------------------------------------------------------*/
/* Read the top of the compare queue, but do not alter the stack. */
/* Return -1 if there is no compare queue. This is a way to */
/* check if the current cells being checked are the topmost in */
/* the queue. Call with all NULL values for a quick check for a */
/* top-level compare. */
/*----------------------------------------------------------------*/
int PeekCompareQueueTop(char **name1, int *file1, char **name2, int *file2)
{
if (CompareQueue == NULL)
return -1;
*name1 = CompareQueue->class1;
*file1 = CompareQueue->file1;
*name2 = CompareQueue->class2;
*file2 = CompareQueue->file2;
if (name1) *name1 = CompareQueue->class1;
if (file1) *file1 = CompareQueue->file1;
if (name2) *name2 = CompareQueue->class2;
if (file2) *file2 = CompareQueue->file2;
return 0;
}
@@ -4328,6 +4331,7 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
// Re-sort list
obp = ob1;
for (i = 0; i < idx1; i++) obp = obp->next;
for (i = 0; i < run; i++) {
obp->next = proplist[i].ob;
obp = obp->next;
@@ -4531,6 +4535,7 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
// Re-sort list
obp = ob1;
for (i = 0; i < idx1; i++) obp = obp->next;
for (i = 0; i < run; i++) {
obp->next = proplist[i].ob;
obp = obp->next;
@@ -5026,21 +5031,30 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
switch(vl->type) {
case PROP_DOUBLE:
case PROP_VALUE:
dval = 2 * fabs(vl->value.dval - vl2->value.dval)
if ((vl2->type == PROP_DOUBLE) || (vl2->type == PROP_VALUE)) {
dval = 2 * fabs(vl->value.dval - vl2->value.dval)
/ (vl->value.dval + vl2->value.dval);
if (dval <= kl->slop.dval) pmatch++;
if (dval <= kl->slop.dval) pmatch++;
}
break;
case PROP_INTEGER:
ival = abs(vl->value.ival - vl2->value.ival);
if (ival <= kl->slop.ival) pmatch++;
if (vl2->type == PROP_INTEGER) {
ival = abs(vl->value.ival - vl2->value.ival);
if (ival <= kl->slop.ival) pmatch++;
}
break;
case PROP_STRING:
if ((*matchfunc)(vl->value.string, vl2->value.string)) pmatch++;
if (vl2->type == PROP_STRING) {
if ((*matchfunc)(vl->value.string, vl2->value.string)) pmatch++;
}
break;
/* will not attempt to match expressions, but it could
/* Will not attempt to match expressions, but it could
* be done with some minor effort by matching each
* stack token and comparing those that are strings.
* Likewise, will not attempt to match properties
* whose types are mismatched, but they could be
* promoted here.
*/
}
}
@@ -7068,12 +7082,15 @@ int IgnoreClass(char *name, int file, unsigned char type)
/* If file1 and file2 are -1, then these are names to */
/* be checked as netcmp works through the hierarchy. */
/* Otherwise, look up the structure for each file and */
/* set the classhash of the second to that of the first */
/* set the classhash of the second to that of the */
/* first. If "dounique" is 1, then give the two cells */
/* their own class hash; they will not be compared */
/* any other cells in either netlist. */
/* */
/* Return 1 on success, 0 on failure */
/*------------------------------------------------------*/
int EquivalenceClasses(char *name1, int file1, char *name2, int file2)
int EquivalenceClasses(char *name1, int file1, char *name2, int file2, int dounique)
{
char *class1, *class2;
struct Correspond *newc;
@@ -7098,16 +7115,19 @@ int EquivalenceClasses(char *name1, int file1, char *name2, int file2)
if (tp->flags & CELL_DUPLICATE)
reverse = 1;
/* Do a cross-check for each name in the other netlist. If */
/* conflicting names exist, then alter the classhash to make it */
/* unique. In the case of duplicate cells, don't do this. */
if (dounique) {
if (!(tp->flags & CELL_DUPLICATE) && !(tp2->flags & CELL_DUPLICATE) &&
/* Do a cross-check for each name in the other netlist. If */
/* conflicting names exist, then alter the classhash to make */
/* it unique. In the case of duplicate cells, don't do this. */
if (!(tp->flags & CELL_DUPLICATE) && !(tp2->flags & CELL_DUPLICATE) &&
!(*matchfunc)(name1, name2)) {
tpx = LookupCellFile(name1, file2);
if (tpx != NULL) need_new_seed = 1;
tpx = LookupCellFile(name2, file1);
if (tpx != NULL) need_new_seed = 1;
tpx = LookupCellFile(name1, file2);
if (tpx != NULL) need_new_seed = 1;
tpx = LookupCellFile(name2, file1);
if (tpx != NULL) need_new_seed = 1;
}
}
/* Now make the classhash values the same so that these cells */
@@ -7422,6 +7442,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
int result = 1, haspins = 0, notempty = 0;
int hasproxy1 = 0, hasproxy2 = 0;
int needclean1 = 0, needclean2 = 0;
int nomatch = 0;
int *correspond;
char *ostr;
#ifdef TCL_NETGEN
Tcl_Obj *mlist, *plist1, *plist2;
@@ -7513,58 +7535,77 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
obp = N2->object;
j = 0;
for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next, j++) {
nomatch = FALSE;
if ((IsPort(ob2)) && (ob2->node == obp->node)) {
if (Debug == 0) {
for (m = 0; m < left_col_end; m++) *(ostr + m) = ' ';
for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' ';
snprintf(ostr, left_col_end, "%s", ob1->name);
if ((*matchfunc)(ob1->name, ob2->name))
}
if ((*matchfunc)(ob1->name, ob2->name)) {
if (Debug == 0)
snprintf(ostr + left_col_end + 1, left_col_end, "%s", ob2->name);
else {
/* Check remainder of ports to see if there is a name match on the
* same net number (multiple ports tied to the same net)
*/
struct objlist *ob3;
for (ob3 = ob2->next, ++j; ob3 != NULL; ob3 = ob3->next, j++) {
if ((IsPort(ob3)) && (ob3->node == ob2->node)) {
if ((*matchfunc)(ob3->name, ob1->name)) {
ob2 = ob3;
}
else {
/* Check remainder of ports to see if there is a name match on the
* same net number (multiple ports tied to the same net)
*/
struct objlist *ob3;
for (ob3 = ob2->next, ++j; ob3 != NULL; ob3 = ob3->next, j++) {
if ((IsPort(ob3)) && (ob3->node == ob2->node)) {
if ((*matchfunc)(ob3->name, ob1->name)) {
ob2 = ob3;
if (Debug == 0)
snprintf(ostr + left_col_end + 1, left_col_end, "%s", ob2->name);
break;
}
break;
}
else {
ob3 = NULL;
break; /* All pins w/the same node should be together */
}
}
if (ob3 == NULL) {
}
else {
ob3 = NULL;
break; /* All pins w/the same node should be together */
}
}
if (ob3 == NULL) {
if (Debug == 0) {
if (ob2->model.port == -1)
snprintf(ostr + left_col_end + 1, left_col_end, "%s **Mismatch**", ob2->name);
else
snprintf(ostr + left_col_end + 1, left_col_end, "(no matching pin)");
/* Pins with different names are on different nets,
* so this should trigger an error return code.
*/
result = 0;
}
nomatch = TRUE;
/* Pins with different names are on different nets,
* so this should trigger an error return code.
*/
result = 0;
}
}
if (Debug == 0) {
for (m = 0; m < right_col_end + 1; m++)
if (*(ostr + m) == '\0') *(ostr + m) = ' ';
Fprintf(stdout, ostr);
}
else if (nomatch) {
Fprintf(stderr, "No matching pin in cell %s for "
"cell %s pin %s\n",
tc2->name, tc1->name, ob1->name);
}
else {
Fprintf(stdout, "Circuit %s port %d \"%s\""
" = cell %s port %d \"%s\"\n",
tc1->name, i, obn->name,
tc2->name, j, obp->name);
tc1->name, i, ob1->name,
tc2->name, j, ob2->name);
}
#ifdef TCL_NETGEN
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
Tcl_NewStringObj(obn->name, -1));
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj(obp->name, -1));
Tcl_NewStringObj(ob1->name, -1));
if (nomatch)
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj("(no matching pin)", -1));
else
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj(ob2->name, -1));
}
#endif
ob2->model.port = i; /* save order */
@@ -7675,6 +7716,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
/* legal SPICE. */
ob1 = tc1->cell;
correspond = (int *)CALLOC((tc1->nodename_cache_maxnodenum + 1), sizeof(int));
for (i = 0; i < numorig; i++) {
bangptr1 = strrchr(ob1->name, '!');
@@ -7717,7 +7760,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
(tc2->flags & CELL_PLACEHOLDER)) ||
(NodeClasses == NULL))) {
ob2->model.port = i; /* save order */
ob2->model.port = i; /* save order */
*(cover + i) = (char)1;
if (Debug == 0) {
@@ -7746,6 +7789,63 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
if (bangptr2) *bangptr2 = '!';
break;
}
else if ((ob1->node != -1) && (ob2->node != -1)) {
/* Check for the case of ports on both sides being shorted
* together. That means that the nodes on both sides connect
* only to ports, that they connect to the same number of ports,
* and that each port pair has a matching name.
*/
int onlyports = 1;
struct objlist *oba, *obb;
for (oba = tc1->cell; oba != NULL; oba = oba->next) {
if ((oba->node == ob1->node) && (oba->type != PORT)) {
onlyports = 0;
break;
}
}
if (onlyports) {
for (obb = tc2->cell; obb != NULL; obb = obb->next) {
if ((obb->node == ob2->node) && (obb->type != PORT)) {
onlyports = 0;
break;
}
}
}
if (onlyports) {
if ((correspond[ob1->node] == 0) || (correspond[ob1->node] == ob2->node)) {
correspond[ob1->node] = ob2->node; /* remember corresponding node */
ob2->model.port = i; /* save order */
*(cover + i) = (char)1;
if (Debug == 0) {
for (m = 0; m < left_col_end; m++) *(ostr + m) = ' ';
for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' ';
snprintf(ostr, left_col_end, "%s", ob1->name);
snprintf(ostr + left_col_end + 1, left_col_end, "%s", ob2->name);
for (m = 0; m < right_col_end + 1; m++)
if (*(ostr + m) == '\0') *(ostr + m) = ' ';
Fprintf(stdout, ostr);
}
else {
Fprintf(stdout, "Circuit %s port %d \"%s\""
" = cell %s port %d \"%s\"\n",
tc1->name, i, ob1->name,
tc2->name, j, ob2->name);
}
#ifdef TCL_NETGEN
if (dolist) {
Tcl_ListObjAppendElement(netgeninterp, plist1,
Tcl_NewStringObj(ob1->name, -1));
Tcl_ListObjAppendElement(netgeninterp, plist2,
Tcl_NewStringObj(ob2->name, -1));
}
#endif
}
if (bangptr2) *bangptr2 = '!';
break;
}
}
}
if (bangptr2) *bangptr2 = '!';
j++;
@@ -7754,6 +7854,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
ob1 = ob1->next;
if (bangptr1) *bangptr1 = '!';
}
FREE(correspond);
/* Find the end of the pin list in tc1, for adding proxy pins */
+2 -1
View File
@@ -38,7 +38,8 @@ extern int PermuteSetup(char *model, int filenum, char *pin1, char *pin2);
extern int PermuteForget(char *model, int filenum, char *pin1, char *pin2);
extern int EquivalenceElements(char *name1, int file1, char *name2, int file2);
extern int EquivalenceNodes(char *name1, int file1, char *name2, int file2);
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2);
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2,
int dounique);
extern int IgnoreClass(char *name, int file, unsigned char type);
extern int MatchPins(struct nlist *tp1, struct nlist *tp2, int dolist);
extern int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run,
+35 -12
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. */
@@ -597,7 +611,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 +628,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;
}
}
@@ -717,7 +737,7 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
if (*s == '\\') {
s++;
while (*s != '\0') {
if ((*s == ' ') || (*s == '\\')) {
if ((*s == ' ') || ((*s == '\\') && (*(s + 1) == '\0'))) {
s++;
break;
}
@@ -866,7 +886,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 +894,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 +912,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},
+3
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"
@@ -47,5 +48,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 */
+1
View File
@@ -191,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);
+366 -17
View File
@@ -42,6 +42,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <stdlib.h> /* for calloc(), free(), getenv() */
#include <ctype.h> /* for isalnum() */
#ifndef IBMPC
#include <sys/types.h> /* for getpwnam() tilde expansion */
#include <pwd.h>
@@ -63,6 +64,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#define VLOG_EQUATION_DELIMITERS "X///**/#((**)X,;:(){}[]=+-*/"
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
#define VLOG_INTEGER_DELIMITERS "X///**/X;[]"
// Used by portelement structure "flags" record.
#define PORT_NOT_FOUND 0
@@ -83,6 +85,16 @@ struct bus {
int end;
};
// Global storage for a 'for' loop
struct _loop {
char *loopvar;
int start;
int end;
long filepos;
};
struct _loop loop;
// Free a bus structure in the hash table during cleanup
int freebus (struct hashlist *p)
@@ -244,7 +256,16 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
for (texp = start; texp; texp = texp->next) {
if ((texp->last != NULL) && (texp->next != NULL) && (texp->oper != '\0')) {
if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
/* Watch for (a)*b+c or a+b*(c); multiplies must be solved first */
if ((texp->last->last != NULL) && ((texp->last->last->oper == '*')
|| (texp->last->last->oper == '/'))) {
/* Do nothing */
}
else if ((texp->next->next != NULL) && ((texp->next->next->oper == '*')
|| (texp->next->next->oper == '/'))) {
/* Do nothing */
}
else if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
if (texp->oper == '-') {
/* Subtract */
texp->last->value -= texp->next->value;
@@ -290,6 +311,7 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
}
}
}
}
/* If only one numerical item remains, then place it in valptr and return 1 */
@@ -311,6 +333,134 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
return 0;
}
//-------------------------------------------------------------------------
// Parse an expression which must resolve to a single integer.
// If it does, return "1" and put the integer in the return
// pointer "iptr". If not, return "0" and iptr is undefined.
// The expression may use parameters, standard arithmetic,
// and parenthetic grouping. This routine does not tokenize input
// but assumes that the entire expression is in the string "expr".
//-------------------------------------------------------------------------
int ParseIntegerExpression(char *expr, int *iptr)
{
int result, value;
char *sptr, *cptr, savec;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
stack = NULL;
savec = '\0';
sptr = expr;
result = 1;
while (sptr && (*sptr != '\0')) {
// Move sptr to first non-space character
while (isspace(*sptr)) sptr++;
// Tokenize. Look ahead to next delimeter and truncate string there.
cptr = sptr + 1;
if (isalnum(*sptr) || (*sptr == '_') || (*sptr == '$')) {
while (*cptr != '\0') {
if (isalnum(*cptr)) cptr++;
else if ((*cptr == '_') || (*cptr == '$')) cptr++;
else break;
}
}
savec = *cptr;
*cptr = '\0';
if (match(sptr, "+") || match(sptr, "-")
|| match(sptr, "*") || match(sptr, "/")
|| match(sptr, "(") || match(sptr, ")")) {
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
newexp->oper = *sptr;
newexp->value = 0;
newexp->next = NULL;
newexp->last = stack;
if (stack) stack->next = newexp;
stack = newexp;
sptr = cptr;
*cptr = savec;
continue;
}
if ((result = sscanf(sptr, "%d", &value)) != 1) {
// Is name in the parameter list?
kl = (struct property *)HashLookup(nexttok, &verilogparams);
if (kl == NULL) {
Printf("Value %s in expression is not a number or a parameter.\n",
sptr);
value = 0;
break;
}
else {
if (kl->type == PROP_STRING) {
result = sscanf(kl->pdefault.string, "%d", &value);
if (result != 1) {
Printf("Parameter %s has value %s that cannot be parsed"
" as an integer.\n",
nexttok, kl->pdefault.string);
value = 0;
break;
}
}
else if (kl->type == PROP_INTEGER) {
value = kl->pdefault.ival;
}
else if (kl->type == PROP_DOUBLE) {
value = (int)kl->pdefault.dval;
if ((double)value != kl->pdefault.dval) {
Printf("Parameter %s has value %g that cannot be parsed"
" as an integer.\n",
nexttok, kl->pdefault.dval);
value = 0;
break;
}
}
else {
Printf("Parameter %s has unknown type; don't know how"
" to parse.\n", nexttok);
value = 0;
break;
}
}
}
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
newexp->oper = '\0';
newexp->value = value;
newexp->next = NULL;
newexp->last = stack;
if (stack) stack->next = newexp;
stack = newexp;
/* Move to next token */
sptr = cptr;
*cptr = savec;
}
if (result != 0) {
if (stack == NULL) {
Printf("Empty array found.\n");
result = 0;
}
else if (EvalExpr(&stack, iptr) != 1) {
Printf("Bad expression found in array.\n");
result = 0;
}
}
/* In case of error, stack may need cleaning up */
while (stack != NULL) {
newexp = stack;
stack = stack->last;
FREE(newexp);
}
return result;
}
//-------------------------------------------------------------------------
// Get bus indexes from the notation name[a:b]. If there is only "name"
// then look up the name in the bus hash list and return the index bounds.
@@ -323,7 +473,6 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
int GetBusTok(struct bus *wb)
{
int result, start, end, value;
char oper;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
@@ -338,7 +487,6 @@ int GetBusTok(struct bus *wb)
if (match(nexttok, "[")) {
start = end = -1;
oper = '\0';
while (nexttok) {
SkipTokComments(VLOG_EQUATION_DELIMITERS);
if (match(nexttok, "]")) {
@@ -467,13 +615,16 @@ int GetBusTok(struct bus *wb)
//--------------------------------------------------------------------
// GetBus() is similar to GetBusTok() (see above), but it parses from
// a string instead of the input tokenizer.
// a string instead of the input tokenizer, and expressions are not
// allowed.
//--------------------------------------------------------------------
int GetBus(char *astr, struct bus *wb)
{
char *colonptr, *brackstart, *brackend, *sigend, sdelim, *aastr;
int result, start, end;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
if (wb == NULL) return 0;
else {
@@ -531,7 +682,7 @@ int GetBus(char *astr, struct bus *wb)
*brackend = '\0';
colonptr = strvchr(aastr, ':');
if (colonptr) *colonptr = '\0';
result = sscanf(brackstart + 1, "%d", &start);
result = ParseIntegerExpression(brackstart + 1, &start);
if (colonptr) *colonptr = ':';
if (result != 1) {
Printf("Badly formed array notation \"%s\"\n", astr);
@@ -539,7 +690,7 @@ int GetBus(char *astr, struct bus *wb)
return 1;
}
if (colonptr)
result = sscanf(colonptr + 1, "%d", &end);
result = ParseIntegerExpression(colonptr + 1, &end);
else {
result = 1;
end = start; // Single bit
@@ -866,16 +1017,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];
char inst[MAX_STR_LEN], model[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN];
struct nlist *tp;
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 */
@@ -1416,6 +1568,145 @@ skip_endmodule:
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
continue;
}
else if (match(nexttok, "end")) {
/* Handle a 'for' loop */
if (loop.loopvar != NULL) {
int loopval;
struct property *klr;
klr = (struct property *)HashLookup(loop.loopvar, &verilogparams);
loopval = klr->pdefault.ival;
if (loopval < loop.end) loopval++;
else if (loopval > loop.end) loopval--;
SeekFile(loop.filepos);
if (loopval == loop.end)
{
HashDelete(loop.loopvar, &verilogparams);
FREE(klr);
FREE(loop.loopvar);
loop.loopvar = NULL;
}
else
klr->pdefault.ival = loopval;
}
continue;
}
else if (match(nexttok, "begin") || match(nexttok, "generate")) {
/* 'generate' section or 'for' loop start is ignored */
continue;
}
else if (match(nexttok, "generate") || match(nexttok, "endgenerate")) {
/* 'generate' section is ignored */
continue;
}
else if (match(nexttok, "genvar")) {
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
continue;
}
else if (match(nexttok, "for")) {
char limittype = '\0';
char *paramkey = NULL;
struct property *kl = NULL, *klr;
/* Parse out the loop variable and count. Set the loop
* variable as a parameter. Find the beginning of the
* loop block and record the file position so that the
* block can be re-parsed for each loop iteration.
*/
SkipTokNoNewline(VLOG_DELIMITERS);
if ((nexttok == NULL) || !match(nexttok, "(")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
break;
}
SkipTokNoNewline(VLOG_DELIMITERS);
/* Next token must be the loop variable */
if (nexttok == NULL) break;
paramkey = strsave(nexttok);
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, "=")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
break;
}
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
if (ParseIntegerExpression(nexttok, &ival) == 0) {
FREE(paramkey);
break;
}
kl = NewProperty();
kl->type = PROP_INTEGER;
kl->slop.ival = 0;
kl->pdefault.ival = ival;
kl->key = paramkey;
kl->idx = 0;
kl->merge = MERGE_NONE;
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, ";")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Assuming a standard 'for' loop here */
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, paramkey)) {
Printf("Don't know how to parse this 'for' loop!\n");
FREE(paramkey);
FREE(kl);
break;
}
SkipTokNoNewline(VLOG_DELIMITERS);
if (strlen(nexttok) == 1) limittype = *nexttok;
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
if (ParseIntegerExpression(nexttok, &ival) == 0) {
FREE(kl);
FREE(paramkey);
break;
}
/* Loops will stop after the last value, so if value is > or <,
* then adjust the end value accordingly.
*/
if (limittype == '<')
ival--;
else if (limittype == '>')
ival++;
SkipTokNoNewline(VLOG_DELIMITERS);
if (!match(nexttok, ";")) {
Printf("Badly formed 'for' loop.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Assume a standard 'for' loop and skip to the block begin */
/* To do: Parse out the loop increment value. */
while (1) {
SkipTokNoNewline(VLOG_DELIMITERS);
if (EndParseFile()) break;
else if (match(nexttok, "begin")) break;
}
if (EndParseFile()) {
Printf("Badly formed 'for' loop: No begin/end block.\n");
FREE(paramkey);
FREE(kl);
break;
}
/* Save the loop variable, ending value, and file position */
loop.loopvar = paramkey;
loop.start = kl->pdefault.ival;
loop.end = ival;
loop.filepos = TellFile();
/* 'for' loop has been completely parsed, so save the loop variable */
HashPtrInstall(paramkey, kl, &verilogparams);
}
else if (match(nexttok, "wire") || match(nexttok, "assign")) { /* wire = node */
struct bus wb, wb2, *nb;
char nodename[MAX_STR_LEN], noderoot[MAX_STR_LEN];
@@ -1479,7 +1770,7 @@ skip_endmodule:
}
else { /* "assign" */
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
if (GetBus(nexttok, &wb) == 0) {
if (GetBusTok(&wb) == 0) {
char *aptr = strvchr(nexttok, '[');
if (aptr != NULL) {
*aptr = '\0';
@@ -1514,9 +1805,11 @@ skip_endmodule:
// "assign a = b" joins two nets.
// "assign a = {b, c, ...}" creates a bus from components.
// "assign" using any boolean arithmetic is not structural verilog.
// "assign a = {x{b}}" creates a bus by repeating a component.
if (nexttok && match(nexttok, "=")) {
char assignname[MAX_STR_LEN], assignroot[MAX_STR_LEN];
int multiplier = 1;
i = wb.start;
while (1) {
@@ -1525,6 +1818,10 @@ skip_endmodule:
if (match(nexttok, "{")) {
/* RHS is a bundle */
/* Make provisional multiplier active. NOTE: */
/* this is not going to properly handle complex */
/* multipliers like {2{a,b}}. Needs rework. */
if (multiplier < 0) multiplier = -multiplier;
continue;
}
else if (match(nexttok, "}")) {
@@ -1540,7 +1837,7 @@ skip_endmodule:
break;
}
else {
if (GetBus(nexttok, &wb2) == 0) {
if (GetBusTok(&wb2) == 0) {
char *aptr = strvchr(nexttok, '[');
j = wb2.start;
if (aptr != NULL) {
@@ -1556,6 +1853,18 @@ skip_endmodule:
else {
j = -1;
rhs = LookupObject(nexttok, CurrentCell);
/* Check if rhs starts with a signal multiplier */
if (rhs == NULL) {
if (ConvertStringToInteger(nexttok, &multiplier) == 1) {
/* Set multiplier to a negative value to
* indicate that it is provisional, waiting on
* signal specification to follow.
*/
multiplier = -multiplier;
continue;
}
}
}
if ((lhs == NULL) || (rhs == NULL)) {
if (rhs != NULL) {
@@ -1568,7 +1877,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)
@@ -1589,20 +1898,35 @@ skip_endmodule:
if (i != -1)
snprintf(nodename, MAX_STR_LEN, "%s[%d]", noderoot, i);
else
strncpy(nodename, MAX_STR_LEN, lhs->name);
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)
snprintf(assignname, MAX_STR_LEN, "%s[%d]", assignroot, j);
else
strncpy(assignname, MAX_STR_LEN, rhs->name);
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;
}
}
}
@@ -1810,6 +2134,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. */
+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
+23 -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} {
@@ -735,6 +741,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.
+12 -14
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;
@@ -2902,7 +2905,7 @@ _netcmp_equate(ClientData clientData,
struct ElementClass *saveEclass = NULL;
struct NodeClass *saveNclass = NULL;
int file1, file2;
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0, dounique = 0;
Tcl_Obj *tobj1, *tobj2, *tobj3;
while (objc > 1) {
@@ -2918,6 +2921,11 @@ _netcmp_equate(ClientData clientData,
objv++;
objc--;
}
else if (!strcmp(optstart, "unique")) {
dounique = 1;
objv++;
objc--;
}
else
break;
}
@@ -3342,19 +3350,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));