Fairly substantial overhaul of the tokenizing routine to better

handle verilog syntax.  Also:  Added SPICE voltage and current
sources as separate classes (as opposed to being converted to
subcircuits, which was how they were previously handled).  That
allowed voltage sources to be checked for zero value and removed
by shorting the ends together, as was being done for zero value
resistors (note that like zero-value resistors, removal is only
done if removing the component makes a better match than leaving
it in).  In particular, yosys has SPICE netlist output that
converts equality assignments ("assign a = b") into zero-value
voltage sources, so these components need to be treated as
non-physical elements.
This commit is contained in:
Tim Edwards 2019-01-09 20:26:38 -05:00
parent ccf2dc23c9
commit 56b4174646
8 changed files with 94 additions and 66 deletions

View File

@ -1659,7 +1659,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
ecomp = (ECompare *)HashFirst(&compdict);
while (ecomp != NULL) {
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
(ecomp->cell1->class == CLASS_RES)) {
((ecomp->cell1->class == CLASS_RES) ||
(ecomp->cell1->class == CLASS_VSOURCE))) {
int node1 = -1, node2 = -1;
lob = NULL;
for (ob1 = tc1->cell; ob1; ) {
@ -1703,7 +1704,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
if (found) break;
}
if (found) {
Fprintf(stdout, "Removing zero-valued device"
Fprintf(stdout, "Removing zero-valued device "
"%s from cell %s makes a better match\n",
tsub1->name,
tc1->name);
@ -1763,7 +1764,8 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
// Repeat the last section for the other circuit
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) &&
(ecomp->cell2->class == CLASS_RES)) {
((ecomp->cell2->class == CLASS_RES) ||
(ecomp->cell2->class == CLASS_VSOURCE))) {
int node1 = -1, node2 = -1;
lob = NULL;
for (ob2 = tc2->cell; ob2; ) {
@ -1807,7 +1809,7 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
if (found) break;
}
if (found) {
Fprintf(stdout, "Removing zero-valued device"
Fprintf(stdout, "Removing zero-valued device "
"%s from cell %s makes a better match\n",
tsub2->name,
tc2->name);

View File

@ -3728,6 +3728,7 @@ int series_combine(struct objlist *ob1, struct nlist *tp1, int idx1, int run1,
typedef struct _propsort {
double value;
int idx;
unsigned char flags;
struct objlist *ob;
} propsort;
@ -3748,7 +3749,9 @@ static int compsort(const void *p1, const void *p2)
/*--------------------------------------------------------------*/
/* Sort properties of ob1 starting at property idx1 up to */
/* property (idx1 + run). Use series critical property for */
/* sorting. Multiply critical property by S before sort. */
/* sorting. Combine properties by S before sort. Note that */
/* use of "S" implies that the devices are the same in all */
/* properties. */
/* ob1 is the record before the first property. */
/*--------------------------------------------------------------*/
@ -3764,8 +3767,8 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
obn = ob1->next;
for (i = 0; i < idx1; i++) obn = obn->next;
// Create a structure of length (run) to hold critical property
// value and index. Then sort that list, then use the sorted
// Create a structure of length (run) to hold property value
// and index. Then sort that list, then use the sorted
// indexes to sort the actual property linked list.
proplist = (propsort *)MALLOC(run * sizeof(propsort));
@ -4376,16 +4379,16 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
kl->idx = pcount++;
// Set critical property index, if there is one.
// To do: deal with possibility of multiple critical properties
// per instance?
if ((series == FALSE) && (kl->merge & MERGE_P_CRIT)) {
crit = kl->idx;
ctype = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
if (series == FALSE) {
if (kl->merge & MERGE_P_CRIT) crit = kl->idx;
if (kl->merge & (MERGE_P_ADD | MERGE_P_PAR))
ctype = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
}
else if ((series == TRUE) && (kl->merge & MERGE_S_CRIT)) {
crit = kl->idx;
ctype = kl->merge & (MERGE_S_ADD | MERGE_S_PAR);
else if (series == TRUE) {
if (kl->merge & MERGE_S_CRIT) crit = kl->idx;
if (kl->merge & (MERGE_S_ADD | MERGE_S_PAR))
ctype = kl->merge & (MERGE_S_ADD | MERGE_S_PAR);
}
kl = (struct property *)HashNext(&(tp->propdict));

View File

@ -437,7 +437,10 @@ void SpiceSkipNewLine(void)
/* delimiters (this presumably could be further extended as needed). */
/* so ",;()" would be a valid delimiter set, but to include C-style */
/* comments and verilog-style parameter lists, one would need */
/* ",;()X/**///#(". */
/* "X/**///#(X,;()". Two-character delimiters should go first so that */
/* they have precedence over one-character delimiters. 'X' should be */
/* the first character of the delimiter string in addition to marking */
/* the boundary between two-character and one-character delimiters. */
/*----------------------------------------------------------------------*/
char *strdtok(char *pstring, char *delim1, char *delim2)
@ -477,10 +480,10 @@ char *strdtok(char *pstring, char *delim1, char *delim2)
/* return the token. */
for (s = stoken; *s; s++) {
twofer = FALSE;
for (s2 = delim2; s2 && *s2; s2++) {
twofer = (delim2 && (*delim2 == 'X')) ? TRUE : FALSE;
for (s2 = ((twofer == TRUE) ? delim2 + 1 : delim2); s2 && *s2; s2++) {
if (*s2 == 'X') {
twofer = TRUE;
twofer = FALSE;
continue;
}
if (twofer) {

View File

@ -87,8 +87,10 @@ extern int PrematchLists(char *, int, char *, int);
#define CLASS_DIODE 16 /* standard SPICE diode */
#define CLASS_INDUCTOR 17 /* standard SPICE inductor */
#define CLASS_XLINE 18 /* transmission line model */
#define CLASS_MODULE 19 /* sim "x"; black-box subcircuit */
#define CLASS_UNDEF 20 /* not defined; error */
#define CLASS_VSOURCE 19 /* independent voltage source */
#define CLASS_ISOURCE 20 /* independent current source */
#define CLASS_MODULE 21 /* sim "x"; black-box subcircuit */
#define CLASS_UNDEF 22 /* not defined; error */
extern char *P(char *fname, char *inststr, char *drain, char *gate, char *source);
extern char *P4(char *fname, char *inststr, char *drain, char *gate, char *source,

View File

@ -108,13 +108,15 @@ struct valuelist {
#define MERGE_P_ADD 0x01 /* Properties sum with device parallel merge */
#define MERGE_P_PAR 0x02 /* Properties add in parallel with parallel merge */
#define MERGE_P_CRIT 0x04 /* This property enables parallel merging */
#define MERGE_P_XCRIT 0x08 /* Old "critical" behavior (deprecated) */
#define MERGE_S_ADD 0x08 /* Properties sum with device series merge */
#define MERGE_S_PAR 0x10 /* Properties add in parallel with series merge */
#define MERGE_S_CRIT 0x20 /* This property enables series merging */
#define MERGE_S_ADD 0x10 /* Properties sum with device series merge */
#define MERGE_S_PAR 0x20 /* Properties add in parallel with series merge */
#define MERGE_S_CRIT 0x40 /* This property enables series merging */
#define MERGE_S_XCRIT 0x80 /* Old "critical" behavior (deprecated) */
#define MERGE_P_MASK (MERGE_P_ADD | MERGE_P_PAR | MERGE_P_CRIT)
#define MERGE_S_MASK (MERGE_S_ADD | MERGE_S_PAR | MERGE_S_CRIT)
#define MERGE_P_MASK (MERGE_P_ADD | MERGE_P_PAR | MERGE_P_CRIT | MERGE_P_XCRIT)
#define MERGE_S_MASK (MERGE_S_ADD | MERGE_S_PAR | MERGE_S_CRIT | MERGE_S_XCRIT)
#define MERGE_ALL_MASK (MERGE_P_MASK | MERGE_S_MASK)
/* Although the above are flags, "ADD" and "PAR" are mutually exclusive. */

View File

@ -117,6 +117,12 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
case CLASS_NPN: case CLASS_PNP: case CLASS_BJT:
spice_class = 'Q';
break;
case CLASS_VSOURCE:
spice_class = 'V';
break;
case CLASS_ISOURCE:
spice_class = 'I';
break;
case CLASS_RES: case CLASS_RES3:
spice_class = 'R';
break;
@ -172,7 +178,7 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
break;
}
/* caps and resistors, print out device value */
/* caps, resistors, voltage and current sources, print out device value */
/* print out device type (model/subcircuit name) */
@ -217,6 +223,23 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
FlushString(" %s", model); /* semiconductor resistor */
break;
case CLASS_VSOURCE:
case CLASS_ISOURCE:
ob = ob->next;
if (ob->type == PROPERTY) {
struct valuelist *vl;
int i;
for (i == 0;; i++) {
vl = &(ob->instance.props[i]);
if (vl->type == PROP_ENDLIST) break;
else if (vl->type == PROP_VALUE) {
FlushString(" %g", vl->value.dval);
break;
}
}
}
break;
default:
FlushString(" %s", model); /* everything else */
}
@ -711,6 +734,12 @@ skip_ends:
else if (!strcasecmp(nexttok, "R")) {
class = CLASS_RES;
}
else if (!strcasecmp(nexttok, "V")) {
class = CLASS_VSOURCE;
}
else if (!strcasecmp(nexttok, "I")) {
class = CLASS_ISOURCE;
}
else if (!strcasecmp(nexttok, "C")) {
class = CLASS_CAP;
}
@ -1436,7 +1465,7 @@ skip_ends:
Port("pos");
Port("neg");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_MODULE);
SetClass(CLASS_VSOURCE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
}
@ -1490,7 +1519,7 @@ skip_ends:
Port("pos");
Port("neg");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_MODULE);
SetClass(CLASS_ISOURCE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
}

View File

@ -59,8 +59,8 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
// See netfile.c for explanation of delimiters. 'X'
// separates single-character delimiters from two-character delimiters.
#define VLOG_DELIMITERS ",;:(){}[]=X///**/#("
#define VLOG_PIN_NAME_DELIMITERS "()X///**/"
#define VLOG_DELIMITERS "X///**/#((**)X,::(){}[]="
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
// Global storage for verilog parameters
struct hashdict verilogparams;
@ -1094,7 +1094,9 @@ skip_endmodule:
if (LookupObject(nexttok, CurrentCell) == NULL)
Node(nexttok);
}
SkipTokNoNewline(VLOG_DELIMITERS);
do {
SkipTokNoNewline(VLOG_DELIMITERS);
} while (nexttok && match(nexttok, ";"));
}
}
else if (match(nexttok, "endmodule")) {
@ -1680,10 +1682,6 @@ char *ReadVerilogTop(char *fname, int *fnum, int blackbox)
kl->pdefault.ival = 1;
HashPtrInstall(kl->key, kl, &verilogdefs);
/* All verilog files should start with a comment line, */
/* but we won't depend upon it. Any comment line */
/* will be handled by the main Verilog file processing. */
ReadVerilogFile(fname, filenum, &CellStack, blackbox);
CloseParseFile();

View File

@ -3227,12 +3227,11 @@ _netcmp_property(ClientData clientData,
};
char *combineoptions[] = {
"none", "par", "add", "par_critical", "add_critical", NULL
"none", "par", "add", "critical", NULL
};
enum CombineOptionIdx {
COMB_NONE_IDX, COMB_PAR_IDX, COMB_ADD_IDX, COMB_PAR_CRITICAL_IDX,
COMB_ADD_CRITICAL_IDX
COMB_NONE_IDX, COMB_PAR_IDX, COMB_ADD_IDX, COMB_CRITICAL_IDX
};
char *yesno[] = {
@ -3268,24 +3267,24 @@ _netcmp_property(ClientData clientData,
break;
case CLASS_RES: case CLASS_RES3:
PropertyMerge(tp->name, tp->file, "w",
MERGE_P_PAR | MERGE_P_CRIT, MERGE_ALL_MASK);
MERGE_P_PAR | MERGE_S_CRIT, MERGE_ALL_MASK);
PropertyMerge(tp->name, tp->file, "l",
MERGE_S_ADD | MERGE_S_CRIT, MERGE_ALL_MASK);
MERGE_S_ADD | MERGE_P_CRIT, MERGE_ALL_MASK);
PropertyMerge(tp->name, tp->file, "value",
MERGE_S_ADD | MERGE_P_PAR, MERGE_ALL_MASK);
tp->flags |= COMB_SERIES;
break;
case CLASS_CAP: case CLASS_ECAP: case CLASS_CAP3:
// NOTE: No attempt to combine area, width, or length;
// only value.
/* NOTE: No attempt to modify perimeter, length, or width */
PropertyMerge(tp->name, tp->file, "area",
MERGE_P_ADD | MERGE_S_PAR, MERGE_ALL_MASK);
PropertyMerge(tp->name, tp->file, "value",
MERGE_P_ADD | MERGE_P_CRIT |
MERGE_S_PAR | MERGE_S_CRIT, MERGE_ALL_MASK);
MERGE_P_ADD | MERGE_S_PAR, MERGE_ALL_MASK);
tp->flags |= COMB_SERIES;
break;
case CLASS_INDUCTOR:
PropertyMerge(tp->name, tp->file, "value",
MERGE_P_PAR | MERGE_P_CRIT |
MERGE_S_PAR | MERGE_S_CRIT, MERGE_ALL_MASK);
MERGE_S_ADD | MERGE_P_PAR, MERGE_ALL_MASK);
tp->flags |= COMB_SERIES;
break;
}
@ -3437,8 +3436,7 @@ _netcmp_property(ClientData clientData,
mergemask = MERGE_S_MASK;
switch (idx2) {
case COMB_NONE_IDX:
mergeval &= ~(MERGE_S_ADD | MERGE_S_PAR
| MERGE_S_CRIT);
mergeval &= ~(MERGE_S_MASK);
tp->flags &= ~COMB_SERIES;
break;
case COMB_PAR_IDX:
@ -3449,12 +3447,8 @@ _netcmp_property(ClientData clientData,
mergeval |= MERGE_S_ADD;
tp->flags |= COMB_SERIES;
break;
case COMB_PAR_CRITICAL_IDX:
mergeval |= (MERGE_S_PAR | MERGE_S_CRIT);
tp->flags |= COMB_SERIES;
break;
case COMB_ADD_CRITICAL_IDX:
mergeval |= (MERGE_S_ADD | MERGE_S_CRIT);
case COMB_CRITICAL_IDX:
mergeval |= MERGE_S_CRIT;
tp->flags |= COMB_SERIES;
break;
}
@ -3463,8 +3457,7 @@ _netcmp_property(ClientData clientData,
mergemask = MERGE_P_MASK;
switch (idx2) {
case COMB_NONE_IDX:
mergeval &= ~(MERGE_P_ADD | MERGE_P_PAR
| MERGE_P_CRIT);
mergeval &= ~(MERGE_P_MASK);
tp->flags |= COMB_NO_PARALLEL;
break;
case COMB_PAR_IDX:
@ -3475,12 +3468,8 @@ _netcmp_property(ClientData clientData,
mergeval |= MERGE_P_ADD;
tp->flags &= ~COMB_NO_PARALLEL;
break;
case COMB_PAR_CRITICAL_IDX:
mergeval |= (MERGE_P_PAR | MERGE_P_CRIT);
tp->flags &= ~COMB_NO_PARALLEL;
break;
case COMB_ADD_CRITICAL_IDX:
mergeval |= (MERGE_P_ADD | MERGE_P_CRIT);
case COMB_CRITICAL_IDX:
mergeval |= MERGE_P_CRIT;
tp->flags &= ~COMB_NO_PARALLEL;
break;
}
@ -3695,7 +3684,7 @@ _netcmp_property(ClientData clientData,
mergeval = MERGE_P_ADD;
break;
case ADD_CRIT_IDX:
mergeval = MERGE_P_ADD | MERGE_P_CRIT;
mergeval = MERGE_P_ADD | MERGE_P_XCRIT;
break;
case PAR_ONLY_IDX:
case PAR2_ONLY_IDX:
@ -3703,12 +3692,12 @@ _netcmp_property(ClientData clientData,
break;
case PAR_CRIT_IDX:
case PAR2_CRIT_IDX:
mergeval = MERGE_P_PAR | MERGE_P_CRIT;
mergeval = MERGE_P_PAR | MERGE_P_XCRIT;
break;
case SER_CRIT_IDX:
case SER2_CRIT_IDX:
case SER3_CRIT_IDX:
mergeval = MERGE_S_ADD | MERGE_S_CRIT;
mergeval = MERGE_S_ADD | MERGE_S_XCRIT;
break;
case SER_IDX:
case SER2_IDX: