Compare commits

...
21 Commits
Author SHA1 Message Date
Tim Edwards 5e48c4e876 Merge branch 'master' into netgen-1.5 2026-06-29 02:00:02 -04:00
R. Timothy Edwards 2c94087510 Corrected an error in delimiter parsing, which is due to the lack of
any standard array delimiter character in SPICE.  Often brackets are
used, but just as often angle brackets are used instead, and do not
match the bracket syntax of verilog.  The "matchnocase" function
automatically casts common delimiter characters to a single type to
facilitate matching between names using different delimiters.
However, some code in MatchPins() was assuming square brackets and
breaking this allowance, causing pins to be marked mismatch in spite
of the handling in "matchnocase".  The code has been corrected to
make the same allowance for different delimiter characters everywhere.
2026-06-28 11:44:42 -04:00
Tim Edwards 0b4a8fe9d6 Merge branch 'master' into netgen-1.5 2026-06-09 02:00:04 -04:00
R. Timothy Edwards 3eadc8b0dc Corrected some issues in the SPICE parser where a subcircuit name
was checked using "subcktname" before its initialization.  Added
a check for a subcircuit called inside itself, which causes an
immediate failure (see issue tracker issue #106).  If allowed to
continue, netgen will either crash or produce inscrutable output
that doesn't pinpoint the issue.
2026-06-08 14:11:07 -04:00
Tim Edwards b3af7b98b4 Merge branch 'master' into netgen-1.5 2026-05-22 02:00:04 -04:00
R. Timothy Edwards 9630670071 The example cited in the last commit is not completely solved by
the code changes in the last commit.  There is still an issue in
which pins should match between name X on one side and X[Y] on
the other side, if there is no X[Z] where Z != Y, specifically if
one or both cells is a black box, since the equivalence cannot
be determined by net matching.

The pin matching has gotten out of hand and really should be
completely redone. . .
2026-05-21 15:35:57 -04:00
R. Timothy Edwards f29452e550 An example found by Leo Moser showed that netgen makes an incorrect
pin assignment when a verilog input file declares a signal "bundle"
with only one signal in it.  The solution is to detect bundles which
have only one component in them, and remove the bundle delimiters
("{...}") so that the pin connection is treated as a simple signal
or vector.
2026-05-21 14:19:24 -04:00
Tim Edwards da3b96ae9e Merge branch 'master' into netgen-1.5 2026-04-28 02:00:02 -04:00
R. Timothy Edwards 8a2bbe0723 Updated version to go along with PR #105 from user jalcim on github. 2026-04-27 09:50:37 -04:00
jalcim a0c49a026a fix(netcmp): correct signal handler type for K&R declaration
netcmp.c:55 declares oldinthandler with empty parameter list:
    void (*oldinthandler)() = SIG_DFL;

In K&R / pre-C23, this means 'function with unspecified parameters'.
GCC 14+ infers void(*)(void), which is incompatible with signal(2)'s
expected void(*)(int) handler. The signal(SIGINT, oldinthandler) calls
at lines 8777 and 8784 then fail with -Wincompatible-pointer-types
(now a default error in GCC 14+).

This 1-line fix matches the actual usage as a SIGINT handler with int
signum parameter, and restores tclnetgen.so build on Fedora 41+ /
Debian 13+ / Ubuntu 24.04+ (any system with GCC 14+).

Tested: tclnetgen.so now builds successfully and 'netgen -batch lvs'
mode works again.
2026-04-26 22:06:07 +02:00
Tim Edwards 1520e276bc Merge branch 'master' into netgen-1.5 2026-04-04 02:00:02 -04:00
R. Timothy Edwards 665203bba1 Corrected genhash() after Mitch Bailey pointed out that the function
was no longer hashing on both values passed to the function, as it
is supposed to.
2026-04-03 08:48:34 -04:00
Tim Edwards f02802dd21 Merge branch 'master' into netgen-1.5 2026-04-03 02:00:04 -04:00
R. Timothy Edwards 0192558d4b Updated version corresponding to the last commit. 2026-04-02 21:30:01 -04:00
R. Timothy Edwards 21d329b22d Modified the hash algorithm used by netgen after a discussion with
ChatGPT about hash implementations.  Switched from SDBM to FNV-1a,
which should be a better/stronger hash algorithm.  Could do
something more sophisticated, but this change can be done in a few
minutes.
2026-04-02 21:29:04 -04:00
Tim Edwards b76c9a1712 Merge branch 'master' into netgen-1.5 2026-02-03 02:00:02 -05:00
R. Timothy Edwards 37b1a2a07d Cleaned up some errors (most minor, some not so minor) in the
code that were surfaced by Stefan Thiede running clang on Mac
OS.  Function prototype warnings have not been fixed yet, as
that is a more involved fix, although it needs to be done.
2026-02-02 20:53:13 -05:00
Tim Edwards 4d44abcca4 Merge branch 'master' into netgen-1.5 2026-01-16 02:00:02 -05:00
R. Timothy Edwards 777f7ef095 Found a counting issue with netcmp output that will overrun the
output string buffer if the size of the copied string is just
the wrong amount, due to the use of strcpy() instead of
strncpy() in at least one place.  Just hacked a solution by
allocating more space for the string, but this should be fixed
properly.  Also:  Discovered that the "zero valued resistor"
routine looks for shorted ports in the wrong place, and if it
finds shorted ports it wrongly decides that the device it's
looking at is a zero-valued resistor whether or not it really
is zero-valued.
2026-01-15 16:39:18 -05:00
Tim Edwards 29ee16644d Merge branch 'master' into netgen-1.5 2025-12-29 02:00:02 -05:00
R. Timothy Edwards 9b4185fe62 Reverted a change from a long time ago regarding removal of zero
valued resistors connecting two ports.  I do not recall exactly
why I put that in but it appears to cause incorrect behavior.
2025-12-28 14:54:07 -05:00
11 changed files with 163 additions and 34 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.313
1.5.322
+2 -2
View File
@@ -91,7 +91,7 @@ char *ActelName(char *Name)
/* strip physical-pin information, if it exists */
if ((nm = strrchr(name,PHYSICALPIN[0])) != NULL) *nm = '\0';
if (strlen(name) > 13) {
ActelIndex = (++ActelIndex) % ACTELNAMESIZE;
ActelIndex = (ActelIndex + 1) % ACTELNAMESIZE;
/* format the value of the hashed value of the string */
sprintf(ActelNames[ActelIndex], "$%lX", ActelNameHash(name));
if (Debug)
@@ -102,7 +102,7 @@ Printf("ActelNameHash returns %s on name %s\n",ActelNames[ActelIndex], name);
NeedsQuoting = 0;
if (NULL != strpbrk(name, ".,:; \t\"'\n\r")) NeedsQuoting = 1;
ActelIndex = (++ActelIndex) % ACTELNAMESIZE;
ActelIndex = (ActelIndex + 1) % ACTELNAMESIZE;
if (!NeedsQuoting) {
strcpy(ActelNames[ActelIndex], name);
return(ActelNames[ActelIndex]);
+15 -4
View File
@@ -2018,8 +2018,11 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
break;
}
}
if (found) break;
}
/* Do NOT remove shorting devices that */
if (found) {
/* Beware remove shorting devices that */
/* connect two ports. Otherwise the */
/* port lists get screwed up. It is */
/* better in that case to force the */
@@ -2027,6 +2030,10 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
/* This is ignored for a top-level cell */
/* because it will just show up as a */
/* port mismatch error as it should. */
/* (12/12/2025---disabling this worked; */
/* may need to go back to a failing */
/* example and determine how pin */
/* matching gets scrambled.) */
if ((not_top == TRUE) &&
(ecomp->cell1->class != CLASS_ISOURCE)) {
@@ -2039,14 +2046,18 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
else if (ob2->node == node2)
found2 = TRUE;
if (found1 && found2) {
found = FALSE;
Fprintf(stdout, "Warning: "
"zero-valued device connects "
"port %s to another port; pin "
"matching may be affected.\n",
ob2->name);
// found = FALSE;
break;
}
}
}
if (found) break;
}
if (found) {
Fprintf(stdout, "Removing zero-valued device "
"%s from cell %s (%d) makes a better "
+20 -13
View File
@@ -137,32 +137,39 @@ static unsigned char uppercase[] = {
// horrible things can happen, as, for example, names AOI12 and OAI12
// have exactly the same hash result. Lousy for binning and even
// lousier for generating class magic numbers.
//
// Updated again 4/2/2026 to the FNV-1a hash, which is better than
// SDBM for this application, according to ChatGPT.
unsigned long hashnocase(char *s, int hashsize)
{
unsigned long hashval;
for (hashval = 0; *s != '\0'; )
hashval = uppercase[*s++]
+ (hashval << 6) + (hashval << 16) - hashval;
unsigned long hashval = 2166136261ul;
for (; *s != '\0'; s++) {
hashval ^= uppercase[*s];
hashval *= 16777619ul;
}
return (hashsize == 0) ? hashval : (hashval % hashsize);
}
unsigned long hashcase(char *s, int hashsize)
{
unsigned long hashval;
for (hashval = 0; *s != '\0'; )
hashval = (*s++) + (hashval << 6) + (hashval << 16) - hashval;
unsigned long hashval = 2166136261ul;
for (; *s != '\0'; s++) {
hashval ^= (unsigned char)(*s);
hashval *= 16777619ul;
}
return (hashsize == 0) ? hashval : (hashval % hashsize);
}
unsigned long genhash(char *s, int c, int hashsize)
{
unsigned long hashval;
for (hashval = (unsigned long)c; *s != '\0'; )
hashval = (*s++) + (hashval << 6) + (hashval << 16) - hashval;
unsigned long hashval = 2166136261ul;
hashval ^= (unsigned long)c;
hashval *= 16777619ul;
for (; *s != '\0'; s++) {
hashval ^= (unsigned char)(*s);
hashval *= 16777619ul;
}
return (hashsize == 0) ? hashval : (hashval % hashsize);
}
+58 -4
View File
@@ -52,7 +52,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef TCL_NETGEN
int InterruptPending = 0;
void (*oldinthandler)() = SIG_DFL;
void (*oldinthandler)(int) = SIG_DFL;
extern Tcl_Interp *netgeninterp;
extern int check_interrupt();
#endif
@@ -1622,8 +1622,8 @@ void FormatIllegalElementClasses()
char *permcount;
int bytesleft;
permname = CALLOC(right_col_end + 2, sizeof(char));
permcount = CALLOC(right_col_end + 2, sizeof(char));
permname = CALLOC(right_col_end + 100, sizeof(char));
permcount = CALLOC(right_col_end + 100, sizeof(char));
ostr = output_string_init();
found = 0;
@@ -7896,7 +7896,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
if (*(cover + i) == (char)0) {
j = 0;
for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next) {
char *name1, *name2;
char *name1, *name2, *aptr1 = NULL, *aptr2 = NULL;
char delim1, delim2;
if (!IsPort(ob2)) break;
@@ -7914,8 +7915,59 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
if (!strncmp(name1, "proxy", 5) && (ob2->node == -1)) name1 +=5;
if (!strncmp(name2, "proxy", 5) && (ob1->node == -1)) name2 +=5;
/* Recognize an array as matching a non-array of the same */
/* name if the array is size 1. There must be a better way */
/* to do this; this method makes excessive calls to */
/* matchfunc(). */
if ((tc1->flags & CELL_PLACEHOLDER) || (tc2->flags && CELL_PLACEHOLDER)) {
aptr1 = get_array_delimiter(name1, &delim1);
aptr2 = get_array_delimiter(name2, &delim2);
if ((aptr1 != NULL) && (aptr2 == NULL)) {
*aptr1 = '\0';
if ((*matchfunc)(name1, name2)) {
/* Check that name1 does not have another array
* component in ob1
*/
struct objlist *ob3;
for (ob3 = tc1->cell; ob3; ob3 = ob3->next) {
if (!IsPort(ob3)) break;
if (ob3 == ob1) continue;
if (!strncmp(ob3->name, name1, strlen(name1)) &&
is_delimiter(*(ob3->name + strlen(name1)))) {
*aptr1 = delim1;
break;
}
}
}
}
if ((aptr1 == NULL) && (aptr2 != NULL)) {
*aptr2 = '\0';
if ((*matchfunc)(name1, name2)) {
/* Check that name2 does not have another array
* component in ob1
*/
struct objlist *ob3;
for (ob3 = tc2->cell; ob3; ob3 = ob3->next) {
if (!IsPort(ob3)) break;
if (ob3 == ob2) continue;
if (!strncmp(ob3->name, name2, strlen(name2)) &&
is_delimiter(*(ob3->name + strlen(name2)))) {
*aptr2 = delim2;
break;
}
}
}
}
}
if ((*matchfunc)(name1, name2)) {
if (aptr1) *aptr1 = delim1;
if (aptr2) *aptr2 = delim2;
/* If both sides have unconnected nodes, then pins with */
/* matching names are an automatic match. Otherwise, if */
/* matching black-box entries, then pins are always */
@@ -8069,6 +8121,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist)
}
}
if (bangptr2) *bangptr2 = '!';
if (aptr1) *aptr1 = delim1;
if (aptr2) *aptr2 = delim2;
j++;
}
}
+2
View File
@@ -685,6 +685,7 @@ void SpiceSkipNewLine(void)
ungetc(contline, infile);
}
#if 0 /* Commented with "#if 0" due to comment characters in the comment */
/*----------------------------------------------------------------------*/
/* Function similar to strtok() for token parsing. The difference is */
/* that it takes two sets of delimiters. The first is whitespace */
@@ -705,6 +706,7 @@ void SpiceSkipNewLine(void)
/* the first character of the delimiter string in addition to marking */
/* the boundary between two-character and one-character delimiters. */
/*----------------------------------------------------------------------*/
#endif
char *strdtok0(char *pstring, char *delim1, char *delim2, char isverilog)
{
+32 -1
View File
@@ -282,6 +282,33 @@ int matchfilenocase(char *st1, char *st2, int f1, int f2)
return 1;
}
/* Delimiter matching---Find an opening delimiter in a string. */
/* Return the position of the first opening delimeter in the string, */
/* like strchr(). Place the actual delimeter character in *delim. */
char *get_array_delimiter(char *name, char *delim)
{
char *stest = name;
while (*stest != '\0')
{
if (to_lower[*stest] == '<') {
*delim = *stest;
return stest;
}
stest++;
}
return NULL;
}
/* Delimiter parsing---Check if a character is an opening array delimiter. */
/* Return TRUE if the character is an opening delimiter, FALSE if not. */
int is_delimiter(char testc)
{
return (to_lower[testc] == '<') ? TRUE : FALSE;
}
#ifdef HAVE_MALLINFO
void PrintMemoryStats(void)
{
@@ -449,6 +476,7 @@ int removeshorted(struct hashlist *p, int file)
ob = nob;
}
}
return 1;
}
/* Remove shorted instances of class "class" from the database */
@@ -539,6 +567,7 @@ int deleteclass(struct hashlist *p, int file)
}
}
FREE(checknodes);
return 1;
}
/* Remove all instances of class "class" from the database */
@@ -576,6 +605,7 @@ int renameinstances(struct hashlist *p, int file)
}
}
}
return 1;
}
void InstanceRename(char *from, char *to, int file)
@@ -598,9 +628,10 @@ int freeprop(struct hashlist *p)
struct property *prop;
prop = (struct property *)(p->ptr);
if (prop->type == PROP_STRING)
if (prop->type == PROP_STRING) {
if (prop->pdefault.string != NULL)
FREE(prop->pdefault.string);
}
else if (prop->type == PROP_EXPRESSION) {
struct tokstack *stackptr, *nptr;
stackptr = prop->pdefault.stack;
+2
View File
@@ -309,6 +309,8 @@ extern int match(char *, char *);
extern int matchnocase(char *, char *);
extern int matchfile(char *, char *, int, int);
extern int matchfilenocase(char *, char *, int, int);
extern int is_delimiter(char);
extern char *get_array_delimiter(char *, char *);
extern void GarbageCollect(void);
extern void InitGarbageCollection(void);
+3 -2
View File
@@ -319,7 +319,7 @@ void Fanout(char *cell, char *node, int filter)
while (ob != NULL) {
char *obname = ob->name;
if (*obname == '/') obname++;
if (ob->node == nodenum)
if (ob->node == nodenum) {
if (filter == ALLOBJECTS) {
Printf(" %s (", obname);
PrintObjectType(ob->type);
@@ -331,6 +331,7 @@ void Fanout(char *cell, char *node, int filter)
else if (ob->type == filter) {
Printf(" %s\n", obname);
}
}
ob = ob->next;
}
}
@@ -933,7 +934,7 @@ static int PrintLeavesInCellHash(struct hashlist *p)
struct nlist *ptr;
ptr = (struct nlist *)(p->ptr);
if ((ptr->class == CLASS_SUBCKT)) PrintLeavesInCell(ptr->name, ptr->file);
if (ptr->class == CLASS_SUBCKT) PrintLeavesInCell(ptr->name, ptr->file);
return(0);
}
+17 -7
View File
@@ -196,7 +196,7 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
if (ob->type == PROPERTY) {
struct valuelist *vl;
int i;
for (i == 0;; i++) {
for (i = 0;; i++) {
vl = &(ob->instance.props[i]);
if (vl->type == PROP_ENDLIST) break;
else if (vl->type == PROP_VALUE) {
@@ -216,7 +216,7 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
if (ob->type == PROPERTY) {
struct valuelist *vl;
int i;
for (i == 0;; i++) {
for (i = 0;; i++) {
vl = &(ob->instance.props[i]);
if (vl->type == PROP_ENDLIST) break;
else if (vl->type == PROP_VALUE) {
@@ -236,7 +236,7 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
if (ob->type == PROPERTY) {
struct valuelist *vl;
int i;
for (i == 0;; i++) {
for (i = 0;; i++) {
vl = &(ob->instance.props[i]);
if (vl->type == PROP_ENDLIST) break;
else if (vl->type == PROP_VALUE) {
@@ -399,7 +399,7 @@ int renamepins(struct hashlist *p, int file)
ptr = (struct nlist *)(p->ptr);
if (ptr->file != file)
return 1;
return 0;
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
if (ob->type == FIRSTPIN) {
@@ -426,6 +426,7 @@ int renamepins(struct hashlist *p, int file)
}
}
}
return 1;
}
/* If any pins are marked unconnected, see if there are */
@@ -1782,10 +1783,19 @@ skip_ends:
if (scan->next != NULL) scan = scan->next;
tail->next = NULL;
/* Check for class defined inside itself (self-referential loop) */
if (!strcasecmp(model, scan->name)) {
Fprintf(stderr, "Fatal: Class \"%s\" is instanced inside of itself!\n",
scan->name);
InputParseError(stderr);
return;
}
/* Check for ignored class */
if ((itype = IsIgnored(subcktname, filenum)) == IGNORE_CLASS) {
Printf("Class '%s' instanced in input but is being ignored.\n", model);
if ((itype = IsIgnored(scan->name, filenum)) == IGNORE_CLASS) {
Printf("Class '%s' instanced in input but is being ignored.\n", scan->name);
return;
}
@@ -1800,7 +1810,7 @@ skip_ends:
break;
}
if (shorted == (unsigned char)1) {
Printf("Instance of '%s' is shorted, ignoring.\n", subcktname);
Printf("Instance of '%s' is shorted, ignoring.\n", scan->name);
while (head) {
p = head->next;
FREE(head);
+11
View File
@@ -2295,7 +2295,18 @@ nextinst:
FREE(wire_bundle);
wire_bundle = new_wire_bundle;
if (!strcmp(nexttok, "}"))
{
/* If a "bundle" has only one component, then it is
* not really a bundle and should be treated as a
* single wire.
*/
if (strchr(wire_bundle, ',') == NULL) {
int blen = strlen(wire_bundle + 1) - 1;
memmove(wire_bundle, wire_bundle + 1, blen);
*(wire_bundle + blen) = '\0';
}
break;
}
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
}
if (!nexttok) {