From 35d338f256c6c20630051e516b0bea2970a799ad Mon Sep 17 00:00:00 2001 From: dwarning Date: Wed, 8 Nov 2023 07:59:36 +0100 Subject: [PATCH 01/48] partly revert commit 423f9a9db - don't remove xspice/icm .c files --- src/xspice/icm/GNUmakefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xspice/icm/GNUmakefile.in b/src/xspice/icm/GNUmakefile.in index 78e578e99..7c01c9b26 100644 --- a/src/xspice/icm/GNUmakefile.in +++ b/src/xspice/icm/GNUmakefile.in @@ -102,7 +102,7 @@ cm-descr := \ cm-clean : -rm -f $(cm)/$(cm).cm -rm -f $(cm-descr) $(cm-objs) $(cm-gens) - -rm -f $(cm)/*/*.o $(cm)/*/*.c $(cm)/*/.deps/* + -rm -f $(cm)/*/*.o $(cm)/*/.deps/* -rm -f $(cm-deps) cm-distclean : From 295f212de8afe7a9bc2013a5baf4139253fb48b1 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Mon, 6 Nov 2023 14:55:02 -0800 Subject: [PATCH 02/48] When CIDER is detected, until KLU works with CIDER, print Error: CIDER simulation is not (yet) supported with 'option klu'. Use 'option sparse' instead. and then call controlled exit(1). --- src/include/ngspice/cidersupt.h | 11 ++++++++++- src/spicelib/devices/nbjt/nbjtset.c | 1 + src/spicelib/devices/nbjt2/nbt2set.c | 2 +- src/spicelib/devices/numd/numdset.c | 1 + src/spicelib/devices/numd2/nud2set.c | 1 + src/spicelib/devices/numos/nummset.c | 2 ++ 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/include/ngspice/cidersupt.h b/src/include/ngspice/cidersupt.h index fd26d80bf..e42f76569 100644 --- a/src/include/ngspice/cidersupt.h +++ b/src/include/ngspice/cidersupt.h @@ -89,6 +89,15 @@ extern BOOLEAN hasSORConverged(double *, double *, int); extern BOOLEAN foundError(int ); extern BOOLEAN compareFiletypeVar(char *); - +#ifdef KLU +#define klu_support_for_cider \ + if (ckt->CKTkluMODE) { \ + fprintf(stderr, "Error: CIDER simulation is not (yet) supported with 'option klu'.\n"); \ + fprintf(stderr, " Use 'option sparse' instead.\n"); \ + controlled_exit(1); \ + } +#else +#define klu_support_for_cider +#endif #endif diff --git a/src/spicelib/devices/nbjt/nbjtset.c b/src/spicelib/devices/nbjt/nbjtset.c index 187dab974..12aa5a696 100644 --- a/src/spicelib/devices/nbjt/nbjtset.c +++ b/src/spicelib/devices/nbjt/nbjtset.c @@ -44,6 +44,7 @@ NBJTsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) ONEmaterial *pM, *pMaterial = NULL, *materialList = NULL; double startTime; + klu_support_for_cider /* loop through all the diode models */ for (; model != NULL; model = NBJTnextModel(model)) { diff --git a/src/spicelib/devices/nbjt2/nbt2set.c b/src/spicelib/devices/nbjt2/nbt2set.c index a39fe7684..1b126b98e 100644 --- a/src/spicelib/devices/nbjt2/nbt2set.c +++ b/src/spicelib/devices/nbjt2/nbt2set.c @@ -47,7 +47,7 @@ NBJT2setup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; - + klu_support_for_cider /* loop through all the models */ for (; model != NULL; model = NBJT2nextModel(model)) { diff --git a/src/spicelib/devices/numd/numdset.c b/src/spicelib/devices/numd/numdset.c index 63748b2e0..fd9efa662 100644 --- a/src/spicelib/devices/numd/numdset.c +++ b/src/spicelib/devices/numd/numdset.c @@ -45,6 +45,7 @@ NUMDsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; + klu_support_for_cider /* loop through all the models */ for (; model != NULL; model = NUMDnextModel(model)) { diff --git a/src/spicelib/devices/numd2/nud2set.c b/src/spicelib/devices/numd2/nud2set.c index 571dcdadc..973b0140d 100644 --- a/src/spicelib/devices/numd2/nud2set.c +++ b/src/spicelib/devices/numd2/nud2set.c @@ -47,6 +47,7 @@ NUMD2setup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; + klu_support_for_cider /* loop through all the models */ for (; model != NULL; model = NUMD2nextModel(model)) { diff --git a/src/spicelib/devices/numos/nummset.c b/src/spicelib/devices/numos/nummset.c index 37f0efc31..0ae7760ff 100644 --- a/src/spicelib/devices/numos/nummset.c +++ b/src/spicelib/devices/numos/nummset.c @@ -47,6 +47,8 @@ NUMOSsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; + klu_support_for_cider + /* loop through all the models */ for (; model != NULL; model = NUMOSnextModel(model)) { if (!model->NUMOSpInfo) { From 0555c5d9c7bc5fd305b8e40d1f25f4af0529154c Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Tue, 7 Nov 2023 11:06:01 -0800 Subject: [PATCH 03/48] Make previous KLU support check more transparent. --- src/include/ngspice/cidersupt.h | 11 ----------- src/spicelib/devices/nbjt/nbjtset.c | 8 +++++++- src/spicelib/devices/nbjt2/nbt2set.c | 8 +++++++- src/spicelib/devices/numd/numdset.c | 9 ++++++++- src/spicelib/devices/numd2/nud2set.c | 8 +++++++- src/spicelib/devices/numos/nummset.c | 8 +++++++- 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/include/ngspice/cidersupt.h b/src/include/ngspice/cidersupt.h index e42f76569..1f13682b8 100644 --- a/src/include/ngspice/cidersupt.h +++ b/src/include/ngspice/cidersupt.h @@ -89,15 +89,4 @@ extern BOOLEAN hasSORConverged(double *, double *, int); extern BOOLEAN foundError(int ); extern BOOLEAN compareFiletypeVar(char *); -#ifdef KLU -#define klu_support_for_cider \ - if (ckt->CKTkluMODE) { \ - fprintf(stderr, "Error: CIDER simulation is not (yet) supported with 'option klu'.\n"); \ - fprintf(stderr, " Use 'option sparse' instead.\n"); \ - controlled_exit(1); \ - } -#else -#define klu_support_for_cider -#endif - #endif diff --git a/src/spicelib/devices/nbjt/nbjtset.c b/src/spicelib/devices/nbjt/nbjtset.c index 12aa5a696..34f62293d 100644 --- a/src/spicelib/devices/nbjt/nbjtset.c +++ b/src/spicelib/devices/nbjt/nbjtset.c @@ -44,7 +44,13 @@ NBJTsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) ONEmaterial *pM, *pMaterial = NULL, *materialList = NULL; double startTime; - klu_support_for_cider +#ifdef KLU + if (ckt->CKTkluMODE) { + fprintf(stderr, "Error: CIDER simulation is not (yet) supported with 'option klu'.\n"); + fprintf(stderr, " Use 'option sparse' instead.\n"); + controlled_exit(1); + } +#endif /* loop through all the diode models */ for (; model != NULL; model = NBJTnextModel(model)) { diff --git a/src/spicelib/devices/nbjt2/nbt2set.c b/src/spicelib/devices/nbjt2/nbt2set.c index 1b126b98e..3164d329f 100644 --- a/src/spicelib/devices/nbjt2/nbt2set.c +++ b/src/spicelib/devices/nbjt2/nbt2set.c @@ -47,7 +47,13 @@ NBJT2setup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; - klu_support_for_cider +#ifdef KLU + if (ckt->CKTkluMODE) { + fprintf(stderr, "Error: CIDER simulation is not (yet) supported with 'option klu'.\n"); + fprintf(stderr, " Use 'option sparse' instead.\n"); + controlled_exit(1); + } +#endif /* loop through all the models */ for (; model != NULL; model = NBJT2nextModel(model)) { diff --git a/src/spicelib/devices/numd/numdset.c b/src/spicelib/devices/numd/numdset.c index fd9efa662..365d290d4 100644 --- a/src/spicelib/devices/numd/numdset.c +++ b/src/spicelib/devices/numd/numdset.c @@ -45,7 +45,14 @@ NUMDsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; - klu_support_for_cider + +#ifdef KLU + if (ckt->CKTkluMODE) { + fprintf(stderr, "Error: CIDER simulation is not (yet) supported with 'option klu'.\n"); + fprintf(stderr, " Use 'option sparse' instead.\n"); + controlled_exit(1); + } +#endif /* loop through all the models */ for (; model != NULL; model = NUMDnextModel(model)) { diff --git a/src/spicelib/devices/numd2/nud2set.c b/src/spicelib/devices/numd2/nud2set.c index 973b0140d..a35a3132f 100644 --- a/src/spicelib/devices/numd2/nud2set.c +++ b/src/spicelib/devices/numd2/nud2set.c @@ -47,7 +47,13 @@ NUMD2setup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; - klu_support_for_cider +#ifdef KLU + if (ckt->CKTkluMODE) { + fprintf(stderr, "Error: CIDER simulation is not (yet) supported with 'option klu'.\n"); + fprintf(stderr, " Use 'option sparse' instead.\n"); + controlled_exit(1); + } +#endif /* loop through all the models */ for (; model != NULL; model = NUMD2nextModel(model)) { diff --git a/src/spicelib/devices/numos/nummset.c b/src/spicelib/devices/numos/nummset.c index 0ae7760ff..7e9838056 100644 --- a/src/spicelib/devices/numos/nummset.c +++ b/src/spicelib/devices/numos/nummset.c @@ -47,7 +47,13 @@ NUMOSsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) DOPtable *dopTableList = NULL; double startTime; - klu_support_for_cider +#ifdef KLU + if (ckt->CKTkluMODE) { + fprintf(stderr, "Error: CIDER simulation is not (yet) supported with 'option klu'.\n"); + fprintf(stderr, " Use 'option sparse' instead.\n"); + controlled_exit(1); + } +#endif /* loop through all the models */ for (; model != NULL; model = NUMOSnextModel(model)) { From 0a087809d6f3fcf782af019bd9ab05194eb2d347 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 8 Nov 2023 13:12:06 +0100 Subject: [PATCH 04/48] If both node names are the same, just print them once in the error message. --- src/maths/ni/niiter.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/maths/ni/niiter.c b/src/maths/ni/niiter.c index 941859b4e..f3e546c5a 100644 --- a/src/maths/ni/niiter.c +++ b/src/maths/ni/niiter.c @@ -174,7 +174,14 @@ NIiter(CKTcircuit *ckt, int maxIter) ckt->CKTstat->STATreorderTime += SPfrontEnd->IFseconds() - startTime; if (error) { SMPgetError(ckt->CKTmatrix, &i, &j); - SPfrontEnd->IFerrorf (ERR_WARNING, "singular matrix: check nodes %s and %s\n", NODENAME(ckt, i), NODENAME(ckt, j)); + if (ft_ngdebug || msgcount < 6) { + SMPgetError(ckt->CKTmatrix, &i, &j); + if (eq(NODENAME(ckt, i), NODENAME(ckt, j))) + SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check node %s\n", NODENAME(ckt, i)); + else + SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check nodes %s and %s\n", NODENAME(ckt, i), NODENAME(ckt, j)); + msgcount += 1; + } /* CKTload(ckt); */ /* SMPprint(ckt->CKTmatrix, stdout); */ From 4228ba6e8a3dc0c4b5d8872edd36e1501ec8a4a5 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Mon, 16 Oct 2023 10:16:59 +0100 Subject: [PATCH 05/48] Ignore end-of-line comments in quoted strings. --- src/frontend/inpcom.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index f3ede2101..8e5ffe1d2 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -3184,8 +3184,6 @@ static void inp_stripcomments_deck(struct card *c, bool cf) If there is only white space before the end-of-line comment the the whole line is converted to a normal comment line (i.e. one that begins with a '*'). - BUG: comment characters in side of string literals are not ignored - ('$' outside of .control section is o.k. however). If the comaptibility mode is PS, LTPS or LTPSA, '$' is treated as a valid character, not as end-of-line comment delimiter, except for that it is @@ -3202,6 +3200,24 @@ static void inp_stripcomments_line(char *s, bool cs) /* look for comments */ while ((c = *d) != '\0') { d++; + + /* Skip over single or double-quoted strings. */ + + if (c == '"') { + while ((c = *d) && (c != '"' || d[-1] == '\\')) + ++d; + if (c) + ++d; + continue; + } + if (c == '\'') { + while ((c = *d) && (c != '\'' || d[-1] == '\\')) + ++d; + if (c) + ++d; + continue; + } + if (*d == ';') { break; } From f6f7319792479bad067a700d1d056de1f31b5ede Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 8 Nov 2023 11:52:12 +0000 Subject: [PATCH 06/48] Add null-pointer checks to some code that crashed when trying to .print results from a non-existent analysis. Also remove the troublesome .plot and .print lines from two examples. --- examples/xspice/original-examples/diffpair.in | 8 +++--- examples/xspice/original-examples/mosmem.in | 4 +-- src/spicelib/analysis/cktacct.c | 27 +++++++++++++------ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/examples/xspice/original-examples/diffpair.in b/examples/xspice/original-examples/diffpair.in index da321ce89..acaf2bad6 100644 --- a/examples/xspice/original-examples/diffpair.in +++ b/examples/xspice/original-examples/diffpair.in @@ -19,10 +19,10 @@ q4 7 7 9 qnl rbias 7 8 20k .model qnl npn(bf=80 rb=100 ccs=2pf tf=0.3ns tr=6ns cje=3pf cjc=2pf + va=50) -.print dc v(4) v(5) -.plot dc v(5) -.print ac vm(5) vp(5) -.plot ac vm(5) vp(5) +*.print dc v(4) v(5) +*.plot dc v(5) +*.print ac vm(5) vp(5) +*.plot ac vm(5) vp(5) .print tran v(4) v(5) .plot tran v(5) .end diff --git a/examples/xspice/original-examples/mosmem.in b/examples/xspice/original-examples/mosmem.in index 2654ca8ec..82e36d96b 100644 --- a/examples/xspice/original-examples/mosmem.in +++ b/examples/xspice/original-examples/mosmem.in @@ -21,7 +21,7 @@ m11 8 4 0 0 mod w=250u l=5u m12 9 9 8 0 mod w=5u l=5u .model mod nmos(vto=0.5 phi=0.7 kp=1.0e-6 gamma=1.83 lambda=0.115 + level=1 cgso=1u cgdo=1u cbd=50p cbs=50p) -.print dc v(5) v(6) -.plot dc v(6) +*.print dc v(5) v(6) +*.plot dc v(6) .plot tran v(6) v(5) v(7) v(1) v(2) .end diff --git a/src/spicelib/analysis/cktacct.c b/src/spicelib/analysis/cktacct.c index 91253f90b..44f3c85c4 100644 --- a/src/spicelib/analysis/cktacct.c +++ b/src/spicelib/analysis/cktacct.c @@ -57,11 +57,18 @@ CKTacct(CKTcircuit *ckt, JOB *anal, int which, IFvalue *val) case OPT_FILLNZ: if ( ckt->CKTmatrix != NULL ) { #ifdef KLU - if (ckt->CKTmatrix->CKTkluMODE) - val->iValue = ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz - - (int)ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNZ ; - else + if (ckt->CKTmatrix->CKTkluMODE) { + if (!ckt->CKTmatrix->SMPkluMatrix || + !ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric) { + return -1; + } + val->iValue = + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz + + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz - + (int)ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNZ ; + } else { val->iValue = spFillinCount(ckt->CKTmatrix->SPmatrix); + } #else val->iValue = spFillinCount(ckt->CKTmatrix->SPmatrix); #endif @@ -72,10 +79,14 @@ CKTacct(CKTcircuit *ckt, JOB *anal, int which, IFvalue *val) case OPT_TOTALNZ: if ( ckt->CKTmatrix != NULL ) { #ifdef KLU - if (ckt->CKTmatrix->CKTkluMODE) - val->iValue = ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz ; - else - val->iValue = spElementCount(ckt->CKTmatrix->SPmatrix); + if (ckt->CKTmatrix->CKTkluMODE && ckt->CKTmatrix->SMPkluMatrix && + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric) { + val->iValue = + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz + + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz ; + } else { + val->iValue = 0; + } #else val->iValue = spElementCount(ckt->CKTmatrix->SPmatrix); #endif From 10386ee3e328c83e6c8083b3d20878a416693a23 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Thu, 9 Nov 2023 11:57:40 +0000 Subject: [PATCH 07/48] Commit [35d338]ngspice partly reverted commit 423f9a9db to fix deletion of source files if make was accidentally used in the source tree. That caused the original bug to return. Fix the problem by deleting the generated source files by name (no such files exist in source. Also make the generated files depend on cmpp, making 'make clean' unnecessary after a chenge to cmpp. --- src/xspice/icm/GNUmakefile.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/xspice/icm/GNUmakefile.in b/src/xspice/icm/GNUmakefile.in index 7c01c9b26..e1344da26 100644 --- a/src/xspice/icm/GNUmakefile.in +++ b/src/xspice/icm/GNUmakefile.in @@ -97,12 +97,12 @@ cm-descr := \ # When recursively making clean, cm-objs and cm-gens do not contain # the files generated for individual code models, as cmpp has already gone -# and modlist and udnlist are empty. Those files are explicitly removed. +# and modlist and udnlist are empty. The files are explicitly removed. cm-clean : -rm -f $(cm)/$(cm).cm -rm -f $(cm-descr) $(cm-objs) $(cm-gens) - -rm -f $(cm)/*/*.o $(cm)/*/.deps/* + -rm -f $(cm)/*/ifspec.c $(cm)/*/cfunc.c $(cm)/*/*.o $(cm)/*/.deps/* -rm -f $(cm-deps) cm-distclean : @@ -135,10 +135,10 @@ $(cm-dirs) $(cm-dep-dirs) : %/cmextrn.h %/cminfo.h %/udnextrn.h %/udninfo.h %/objects.inc : $(srcdir)/%/modpath.lst $(srcdir)/%/udnpath.lst CMPP_IDIR=$(srcdir)/$(@D) CMPP_ODIR=$(@D) $(CMPP) -lst -%/ifspec.c : $(srcdir)/%/ifspec.ifs +%/ifspec.c : $(srcdir)/%/ifspec.ifs $(cmpp) CMPP_IDIR=$(srcdir)/$(@D) CMPP_ODIR=$(@D) $(CMPP) -ifs -%/cfunc.c : $(srcdir)/%/cfunc.mod +%/cfunc.c : $(srcdir)/%/cfunc.mod $(cmpp) CMPP_IDIR=$(srcdir)/$(@D) CMPP_ODIR=$(@D) $(CMPP) -mod From 359e3d46094892519b821ee43671f8a2bff5bb28 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Thu, 9 Nov 2023 12:09:05 +0000 Subject: [PATCH 08/48] Fix Bug #467 - ".OPTION INTERP has accuracy issue". --- src/frontend/outitf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/outitf.c b/src/frontend/outitf.c index d557bf5a3..b4d6955ac 100644 --- a/src/frontend/outitf.c +++ b/src/frontend/outitf.c @@ -1542,6 +1542,7 @@ InterpFileAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr) newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i]; fileAddRealValue(run->fp, run->binary, newval); valueold[i] = valuenew[i]; + timeold = refValue->rValue; } else if (nodata) /* Just keep the transient output value corresponding to timeold, @@ -1573,6 +1574,7 @@ InterpFileAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr) newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i]; fileAddRealValue(run->fp, run->binary, newval); valueold[i] = valuenew[i]; + timeold = refValue->rValue; } else if (nodata) /* Just keep the transient output value corresponding to timeold, @@ -1705,6 +1707,7 @@ InterpPlotAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr) newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i]; plotAddRealValue(&run->data[i], newval); valueold[i] = valuenew[i]; + timeold = refValue->rValue; } else if (nodata) /* Just keep the transient output value corresponding to timeold, @@ -1727,6 +1730,7 @@ InterpPlotAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr) newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i]; plotAddRealValue(&run->data[i], newval); valueold[i] = valuenew[i]; + timeold = refValue->rValue; } else if (nodata) /* Just keep the transient output value corresponding to timeold, From 02aa2c03f971bbac17e737b6d5de2e6b88332e8a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 10 Nov 2023 19:48:52 +0100 Subject: [PATCH 09/48] klu is now compiled-in as standard --- compile_linux_shared.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compile_linux_shared.sh b/compile_linux_shared.sh index 5d28d3930..02e3a0908 100755 --- a/compile_linux_shared.sh +++ b/compile_linux_shared.sh @@ -11,7 +11,7 @@ # --enable-osdi will add the osdi interface which allows to dynamically load compiled Verilog-A # compact models. Compiling the VA code of the models is done by the OpenVAF compiler. # Please see the ngspice manual, chapt. 13, for more info on OSDI/OpenVAF. -# CIDER, XSPICE, and OpenMP may be selected at will. +# CIDER, XSPICE, KLU, and OpenMP may be selected at will. # --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info. # Add (optionally) --enable-relpath to avoid absolute paths when searching for code models. @@ -44,7 +44,7 @@ if test "$1" = "d"; then echo # The --prefix (and perhaps --libdir) may be used to determine a different install location # (depending on the Linux distribution, and on the calling programs search path). - ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --prefix=/usr CFLAGS="-g -m64 -O0 -Wall" LDFLAGS="-m64 -g" + ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --prefix=/usr CFLAGS="-g -m64 -O0 -Wall" LDFLAGS="-m64 -g" else cd releasesh if [ $? -ne 0 ]; then echo "cd releasesh failed"; exit 1 ; fi @@ -52,7 +52,7 @@ else echo # The --prefix (and perhaps --libdir) may be used to determine a different install location # (depending on the Linux distribution, and on the calling programs search path). - ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --disable-debug --enable-osdi --prefix=/usr CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" + ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --disable-debug --prefix=/usr CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" fi if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi From c87df54f24969de0c9d503c349258d9953ae208a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 11 Nov 2023 10:39:51 +0100 Subject: [PATCH 10/48] Remove unused global variable cp_dol. This also prevents a gcc 13 bug, as cp_dol has been set to '\0' by -O2 optimization (see https://stackoverflow.com/questions/77407156/s-0-being-optimized-out-possible-gcc-13-bug-or-some-undefined-behaviour) --- src/frontend/variable.c | 6 ++---- src/include/ngspice/cpextern.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/frontend/variable.c b/src/frontend/variable.c index f4302e52c..3a241bb2a 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -789,9 +789,7 @@ cp_getvar(char *name, enum cp_types type, void *retval, size_t rsize) * isset is TRUE if the variable is being set, FALSE if unset. Also * required is a routine cp_enqvar(name) which returns a struct * variable *, which allows the host program to provide values for - * non-cshpar variables. */ - -char cp_dol = '$'; + * non-cshpar variables. See line 855 */ /* Non-alphanumeric characters that may appear in variable names. < is very * special... @@ -852,7 +850,7 @@ wordlist *cp_variablesubst(wordlist *wlist) char *s_dollar; int i = 0; - while ((s_dollar = strchr(wl->wl_word + i, cp_dol)) != NULL) { + while ((s_dollar = strchr(wl->wl_word + i, '$')) != NULL) { int prefix_len = (int) (s_dollar - wl->wl_word); diff --git a/src/include/ngspice/cpextern.h b/src/include/ngspice/cpextern.h index b051e0bcd..00ab7f7da 100644 --- a/src/include/ngspice/cpextern.h +++ b/src/include/ngspice/cpextern.h @@ -156,7 +156,6 @@ extern bool cp_ignoreeof; extern bool cp_noclobber; extern bool cp_noglob; extern bool cp_nonomatch; -extern char cp_dol; extern void cp_remvar(char *varname); void cp_vset(const char *varname, enum cp_types type, const void *value); extern struct variable *cp_setparse(wordlist *wl); From 56459685104885464e5e035e67d9dc5c0fdb2f9c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 12 Nov 2023 15:40:04 +0100 Subject: [PATCH 11/48] compiling with klu is now standard --- compile_linux.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compile_linux.sh b/compile_linux.sh index 5454ba0ec..0181a08d4 100755 --- a/compile_linux.sh +++ b/compile_linux.sh @@ -16,7 +16,8 @@ # external OpenVAF Verilig-A compiler, will allow access to advanced compact # device models writen in Verilog-A. # Please see the ngspice manual, chapt. 13, for more info on using OSDI/OpenVAF. -# CIDER, XSPICE, and OpenMP may be selected at will. +# --enable-klu will add the new matrix solver in addition to Sparse 1.3. +# CIDER, XSPICE, KLU, and OpenMP may be selected at will. # --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info. # ngspice as shared library: @@ -48,13 +49,13 @@ if test "$1" = "d"; then if [ $? -ne 0 ]; then echo "cd debug failed"; exit 1 ; fi echo "configuring for 64 bit debug" echo - ../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --with-readline=yes --enable-openmp CFLAGS="-g -m64 -O0 -Wall -Wno-unused-but-set-variable" LDFLAGS="-m64 -g" + ../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --enable-klu --with-readline=yes --enable-openmp --prefix="/usr/local" --libdir="/usr/local/lib" CFLAGS="-g -m64 -O0 -Wall -Wno-unused-but-set-variable" LDFLAGS="-m64 -g" else cd release if [ $? -ne 0 ]; then echo "cd release failed"; exit 1 ; fi echo "configuring for 64 bit release" echo - ../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --with-readline=yes --enable-openmp --disable-debug CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" + ../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --enable-klu --with-readline=yes --enable-openmp --disable-debug --prefix="/usr/local" --libdir="/usr/local/lib" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" fi if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi From 2a005292dbc59b9e2ea12172408a37f2dc35812c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 12 Nov 2023 16:32:40 +0100 Subject: [PATCH 12/48] Sparse 1.3 is the matrix solver if no option (sparse or klu) is given. KLU is selected by 'option klu' If both 'option klu' and 'option sparse' are given, the last one executed determines the solver. 'option sparse=0' de-selects spares, i.e. selects klu. 'option klu=0' de-selects klu, selects sparse. 'option klu=1' has the same meaning as 'option klu'. 'option sparse=1' has the same meaning as 'option sparse'. --- src/include/ngspice/optdefs.h | 1 + src/spicelib/analysis/cktntask.c | 2 +- src/spicelib/analysis/cktsopt.c | 6 +++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/include/ngspice/optdefs.h b/src/include/ngspice/optdefs.h index a39f57b23..6e8a5549f 100644 --- a/src/include/ngspice/optdefs.h +++ b/src/include/ngspice/optdefs.h @@ -124,6 +124,7 @@ enum { #ifdef KLU OPT_SPARSE, + OPT_KLU, OPT_KLU_MEMGROW_FACTOR, #endif diff --git a/src/spicelib/analysis/cktntask.c b/src/spicelib/analysis/cktntask.c index 2ad44e3b0..be681b2b6 100644 --- a/src/spicelib/analysis/cktntask.c +++ b/src/spicelib/analysis/cktntask.c @@ -141,7 +141,7 @@ CKTnewTask(CKTcircuit *ckt, TSKtask **taskPtr, IFuid taskName, TSKtask **defPtr) tsk->TSKepsmin = 1e-28; #ifdef KLU - tsk->TSKkluMODE = CKTkluON; + tsk->TSKkluMODE = CKTkluOFF; tsk->TSKkluMemGrowFactor = 1.2 ; #endif diff --git a/src/spicelib/analysis/cktsopt.c b/src/spicelib/analysis/cktsopt.c index d69e878f5..0c1f723e1 100644 --- a/src/spicelib/analysis/cktsopt.c +++ b/src/spicelib/analysis/cktsopt.c @@ -180,7 +180,9 @@ CKTsetOpt(CKTcircuit *ckt, JOB *anal, int opt, IFvalue *val) case OPT_SPARSE: task->TSKkluMODE = (val->iValue == 0); break; - + case OPT_KLU: + task->TSKkluMODE = (val->iValue != 0); + break; case OPT_KLU_MEMGROW_FACTOR: task->TSKkluMemGrowFactor = (val->rValue == 1.2); break; @@ -348,6 +350,8 @@ static IFparm OPTtbl[] = { #ifdef KLU { "sparse", OPT_SPARSE, IF_SET|IF_FLAG, "Set SPARSE 1.3 as Direct Linear Solver" }, + { "klu", OPT_KLU, IF_SET|IF_FLAG, + "Set KLU as Direct Linear Solver" }, { "klu_memgrow_factor", OPT_KLU_MEMGROW_FACTOR, IF_SET|IF_REAL, "KLU Memory Grow Factor (default is 1.2)" } #endif From 2cf2693706afb9390612eb37e61f3cb8f0aa5b6f Mon Sep 17 00:00:00 2001 From: dwarning Date: Sun, 12 Nov 2023 17:04:36 +0100 Subject: [PATCH 13/48] few code cleanings --- src/frontend/inp.c | 5 +++-- src/frontend/inpcom.c | 2 ++ src/maths/cmaths/cmath2.h | 1 + src/xspice/cm/cmexport.c | 2 -- src/xspice/cm/cmutil.c | 2 -- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index aba8450d1..80400988d 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -57,7 +57,7 @@ static wordlist *inp_savecurrents(struct card *deck, struct card *options, wordlist *wl, wordlist *controls); static void recifeval(struct card *pdeck); static char *upper(register char *string); -static void rem_unused_mos_models(struct card* deck); +//static void rem_unused_mos_models(struct card* deck); extern void com_optran(wordlist * wl); extern void tprint(struct card *deck); @@ -2540,7 +2540,7 @@ struct mlist { bool used; bool checked; }; - +#ifdef NONE /* Finally get rid of unused MOS models */ static void rem_unused_mos_models(struct card* deck) { struct card *tmpc, *tmppc = NULL; @@ -2744,3 +2744,4 @@ static void rem_unused_mos_models(struct card* deck) { modellist = tlist; } } +#endif diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 8e5ffe1d2..0993e1712 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -584,6 +584,7 @@ static void delete_names(struct names *p) } #ifndef _MSC_VER +#ifdef CIDER /* concatenate 2 strings, with space if spa == TRUE, return malloced string (replacement for tprintf, which is not efficient enough when reading PDKs @@ -613,6 +614,7 @@ static char *cat2strings(char *s1, char *s2, bool spa) return strsum; } #endif +#endif /* line1 diff --git a/src/maths/cmaths/cmath2.h b/src/maths/cmaths/cmath2.h index 897518721..31bd4c1a0 100644 --- a/src/maths/cmaths/cmath2.h +++ b/src/maths/cmaths/cmath2.h @@ -23,6 +23,7 @@ void * cx_mean(void *data, short int type, int length, int *newlength, short int void * cx_stddev(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_length(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_vector(void *data, short int type, int length, int *newlength, short int *newtype); +void * cx_cvector(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_unitvec(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_plus(void *data1, void *data2, short int datatype1, short int datatype2, int length); void * cx_minus(void *data1, void *data2, short int datatype1, short int datatype2, int length); diff --git a/src/xspice/cm/cmexport.c b/src/xspice/cm/cmexport.c index 0e65a1f75..a644cb921 100644 --- a/src/xspice/cm/cmexport.c +++ b/src/xspice/cm/cmexport.c @@ -3,8 +3,6 @@ #include "ngspice/cm.h" #include "ngspice/dllitf.h" -extern bool cp_getvar(char *, enum cp_types, void *, size_t); - /*how annoying!, needed for structure below*/ static void *tcalloc(size_t a, size_t b) { return tmalloc(a*b); /* FIXME, tcalloc must zero !?!? */ diff --git a/src/xspice/cm/cmutil.c b/src/xspice/cm/cmutil.c index ee69a0f99..88787e8ac 100644 --- a/src/xspice/cm/cmutil.c +++ b/src/xspice/cm/cmutil.c @@ -52,8 +52,6 @@ NON-STANDARD FEATURES #include #include "ngspice/cm.h" -extern void controlled_exit(const int); - /* Corner Smoothing Function ************************************ * * * The following function smooths the transition between two * From 29bfe94318e02165124e7d8fad0c27b7fdc11580 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 13 Nov 2023 12:32:15 +0100 Subject: [PATCH 14/48] Exclude some code under a unified #undef REM_USED --- src/frontend/inp.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index 80400988d..228c63526 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -57,8 +57,9 @@ static wordlist *inp_savecurrents(struct card *deck, struct card *options, wordlist *wl, wordlist *controls); static void recifeval(struct card *pdeck); static char *upper(register char *string); -//static void rem_unused_mos_models(struct card* deck); - +#ifdef REM_UNUSED +static void rem_unused_mos_models(struct card* deck); +#endif extern void com_optran(wordlist * wl); extern void tprint(struct card *deck); @@ -955,14 +956,14 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) /* If user wants all currents saved (.options savecurrents), add .save to wl_first with all terminal currents available on selected devices */ wl_first = inp_savecurrents(deck, options, wl_first, controls); - +#ifdef REM_UNUSED /* Circuit is flat, all numbers expanded. So again try to remove unused MOS models. All binning models are still here when w or l have been determined by an expression. */ -// if (newcompat.hs || newcompat.spe) -// rem_unused_mos_models(deck->nextcard); - + if (newcompat.hs || newcompat.spe) + rem_unused_mos_models(deck->nextcard); +#endif /* now load deck into ft_curckt -- the current circuit. */ if(inp_dodeck(deck, tt, wl_first, FALSE, options, filename) != 0) return 1; @@ -2540,7 +2541,8 @@ struct mlist { bool used; bool checked; }; -#ifdef NONE + +#ifdef REM_UNUSED /* Finally get rid of unused MOS models */ static void rem_unused_mos_models(struct card* deck) { struct card *tmpc, *tmppc = NULL; From 4da3dc51fd25063c55811351ab5e3d73488c8050 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 14 Nov 2023 16:56:31 +0100 Subject: [PATCH 15/48] =?UTF-8?q?If=20compatmode=20ps=20or=20lt:=20add=20a?= =?UTF-8?q?=20series=20resistance=20(100=C2=B5Ohms)=20to=20diodes,=20when?= =?UTF-8?q?=20RS=20is=20not=20given=20or=200:=20Improves=20convergence=20i?= =?UTF-8?q?n=20behavioural=20models=20for=20op=20evaluation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/spicelib/devices/dio/diosetup.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/spicelib/devices/dio/diosetup.c b/src/spicelib/devices/dio/diosetup.c index 2cae77ca0..5373ec79e 100644 --- a/src/spicelib/devices/dio/diosetup.c +++ b/src/spicelib/devices/dio/diosetup.c @@ -16,6 +16,7 @@ Modified by Paolo Nenzi 2003 and Dietmar Warning 2012 #include "ngspice/sperror.h" #include "ngspice/suffix.h" #include "ngspice/fteext.h" +#include "ngspice/compatmode.h" int DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) @@ -195,7 +196,13 @@ DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) } if((!model->DIOresistGiven) || (model->DIOresist==0)) { - model->DIOconductance = 0.0; + if (newcompat.ps || newcompat.lt) { + model->DIOconductance = 1e4; /* improved convergence */ + if (ft_ngdebug) + fprintf(stderr, "Diode series resistance in model %s set to 100 Ohm\n", model->gen.GENmodName); + } + else + model->DIOconductance = 0.0; } else { model->DIOconductance = 1/model->DIOresist; } From 55ba9279739cca83b4129c6316d48a9221b10568 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 14 Nov 2023 16:57:14 +0100 Subject: [PATCH 16/48] If nan occurs during op calculation, issue the warning no more than 10 times. --- src/maths/ni/niconv.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/maths/ni/niconv.c b/src/maths/ni/niconv.c index c147caf43..6acf3688b 100644 --- a/src/maths/ni/niconv.c +++ b/src/maths/ni/niconv.c @@ -26,6 +26,7 @@ NIconvTest(CKTcircuit *ckt) double old; double new; double tol; + static int nancount = 0; node = ckt->CKTnodes; size = SMPmatSize(ckt->CKTmatrix); @@ -41,8 +42,13 @@ NIconvTest(CKTcircuit *ckt) new = ckt->CKTrhs [i] ; old = ckt->CKTrhsOld [i] ; if (isnan(new)) { - if (ft_ngdebug) + if (ft_ngdebug && nancount < 10) { fprintf(stderr, "Warning: non-convergence, node %s is nan\n", CKTnodName(ckt, i)); + nancount++; + } + else if (ft_ngdebug && nancount == 10) { + fprintf(stderr, " non-convergence warnings (nan) limited to 10\n", CKTnodName(ckt, i)); + } return 1; } if(node->type == SP_VOLTAGE) { From 22a10c0e44454614b123dbf9bf1bbf51b5957b4d Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 15 Nov 2023 22:54:06 +0100 Subject: [PATCH 17/48] typo --- src/spicelib/devices/dio/diosetup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spicelib/devices/dio/diosetup.c b/src/spicelib/devices/dio/diosetup.c index 5373ec79e..6bf8e14da 100644 --- a/src/spicelib/devices/dio/diosetup.c +++ b/src/spicelib/devices/dio/diosetup.c @@ -199,7 +199,7 @@ DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states) if (newcompat.ps || newcompat.lt) { model->DIOconductance = 1e4; /* improved convergence */ if (ft_ngdebug) - fprintf(stderr, "Diode series resistance in model %s set to 100 Ohm\n", model->gen.GENmodName); + fprintf(stderr, "Diode series resistance in model %s set to 100 microOhm\n", model->gen.GENmodName); } else model->DIOconductance = 0.0; From 0735b8d0a5f570c0515b5a0c41cf6626d6dd35d2 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 15 Nov 2023 22:54:53 +0100 Subject: [PATCH 18/48] fix previous commit on repeating error messages --- src/maths/ni/niconv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/maths/ni/niconv.c b/src/maths/ni/niconv.c index 6acf3688b..519f039d3 100644 --- a/src/maths/ni/niconv.c +++ b/src/maths/ni/niconv.c @@ -48,6 +48,7 @@ NIconvTest(CKTcircuit *ckt) } else if (ft_ngdebug && nancount == 10) { fprintf(stderr, " non-convergence warnings (nan) limited to 10\n", CKTnodName(ckt, i)); + nancount++; } return 1; } From d060cb82afb467db99f704f23bb60f41bb30a0a3 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 17 Nov 2023 13:57:39 +0100 Subject: [PATCH 19/48] typo --- src/osdi/osdiregistry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osdi/osdiregistry.c b/src/osdi/osdiregistry.c index 1fb37ca17..db9097567 100644 --- a/src/osdi/osdiregistry.c +++ b/src/osdi/osdiregistry.c @@ -335,7 +335,7 @@ extern OsdiObjectFile load_object_file(const char *input) { * multiple times. We use the handle as a key because the same SO will always * return the SAME pointer as long as dlclose is not called. * nghash_insert returns NULL if the key (handle) was not already in the table - * and the data (DUMMYDATA) that was previously insered (!= NULL) otherwise*/ + * and the data (DUMMYDATA) that was previously inserted (!= NULL) otherwise*/ if (nghash_insert(known_object_files, handle, DUMMYDATA)) { txfree(path); return EMPTY_OBJECT; From 2bbadc6085a888e250b8329df38442f761925392 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 17 Nov 2023 13:58:57 +0100 Subject: [PATCH 20/48] Use a hash table glonodes to prevent double or multiple entries in global node table global_nodes --- src/frontend/subckt.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c index 6de79afd8..f70d463fd 100644 --- a/src/frontend/subckt.c +++ b/src/frontend/subckt.c @@ -61,6 +61,7 @@ Modified: 2000 AlansFixes #include "ngspice/fteinp.h" #include "ngspice/stringskip.h" #include "ngspice/compatmode.h" +#include "ngspice/hash.h" #include @@ -104,6 +105,11 @@ static int inp_numnodes(char c); #define N_GLOBAL_NODES 1005 +/* hash table to store the global nodes + * For now its use is limited to avoid double entries in global_nodes[] */ +static NGHASHPTR glonodes = NULL; +#define DUMMYDATA ((void *)42) + /*--------------------------------------------------------------------- * table is used in settrans and gettrans -- it holds the netnames used * in the .subckt definition (t_old), and in the subcircuit invocation @@ -144,29 +150,40 @@ static int num_global_nodes; static void collect_global_nodes(struct card *c) { + glonodes = nghash_init(NGHASH_MIN_SIZE); + num_global_nodes = 0; global_nodes[num_global_nodes++] = copy("0"); + nghash_insert(glonodes, global_nodes[num_global_nodes - 1], DUMMYDATA); #ifdef XSPICE global_nodes[num_global_nodes++] = copy("null"); + nghash_insert(glonodes, global_nodes[num_global_nodes - 1], DUMMYDATA); #endif + + for (; c; c = c->nextcard) if (ciprefix(".global", c->line)) { char *s = c->line; s = nexttok(s); while (*s) { if (num_global_nodes == N_GLOBAL_NODES) { - fprintf(stderr, "ERROR, N_GLOBAL_NODES overflow\n"); + fprintf(stderr, "ERROR, the number of global nodes is limited to %d\n", N_GLOBAL_NODES); controlled_exit(EXIT_FAILURE); } char *t = skip_non_ws(s); - global_nodes[num_global_nodes++] = copy_substring(s, t); + char *gnode = copy_substring(s, t); + if (nghash_find(glonodes, gnode) == NULL) { + global_nodes[num_global_nodes++] = gnode; + nghash_insert(glonodes, gnode, DUMMYDATA); + } s = skip_ws(t); } c->line[0] = '*'; /* comment it out */ } + nghash_free(glonodes, NULL, NULL); #ifdef TRACE { From 81bb886c7153c49d9c940daf88274c60c40bf0c6 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 17 Nov 2023 17:02:06 +0100 Subject: [PATCH 21/48] Replace array global_nodes by hash table glonodes: Save each global node only once Search for global nodes hash table (much faster than scanning the list repeatedly). Remove limit for amount of global nodes. --- src/frontend/subckt.c | 63 +++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c index f70d463fd..7da42f993 100644 --- a/src/frontend/subckt.c +++ b/src/frontend/subckt.c @@ -96,15 +96,13 @@ static int translate(struct card *deck, char *formal, int flen, char *actual, struct bxx_buffer; static void finishLine(struct bxx_buffer *dst, char *src, char *scname); static int settrans(char *formal, int flen, char *actual, const char *subname); -static char *gettrans(const char *name, const char *name_end); +static char *gettrans(const char *name, const char *name_end, bool *isglobal); static int numnodes(const char *line, struct subs *subs, wordlist const *modnames); static int numdevs(char *s); static wordlist *modtranslate(struct card *deck, char *subname, wordlist *new_modnames); static void devmodtranslate(struct card *deck, char *subname, wordlist * const orig_modnames); static int inp_numnodes(char c); -#define N_GLOBAL_NODES 1005 - /* hash table to store the global nodes * For now its use is limited to avoid double entries in global_nodes[] */ static NGHASHPTR glonodes = NULL; @@ -143,47 +141,35 @@ static bool use_numparams = FALSE; static char start[32], sbend[32], invoke[32], model[32]; -static char *global_nodes[N_GLOBAL_NODES]; -static int num_global_nodes; - - static void collect_global_nodes(struct card *c) { + /* hash table for global nodes */ glonodes = nghash_init(NGHASH_MIN_SIZE); - num_global_nodes = 0; - - global_nodes[num_global_nodes++] = copy("0"); - nghash_insert(glonodes, global_nodes[num_global_nodes - 1], DUMMYDATA); - + /* add 0 and null as global nodes */ + nghash_insert(glonodes, "0", DUMMYDATA); #ifdef XSPICE - global_nodes[num_global_nodes++] = copy("null"); - nghash_insert(glonodes, global_nodes[num_global_nodes - 1], DUMMYDATA); + nghash_insert(glonodes, "null", DUMMYDATA); #endif - - for (; c; c = c->nextcard) if (ciprefix(".global", c->line)) { char *s = c->line; s = nexttok(s); while (*s) { - if (num_global_nodes == N_GLOBAL_NODES) { - fprintf(stderr, "ERROR, the number of global nodes is limited to %d\n", N_GLOBAL_NODES); - controlled_exit(EXIT_FAILURE); - } char *t = skip_non_ws(s); + /* global node name */ char *gnode = copy_substring(s, t); - if (nghash_find(glonodes, gnode) == NULL) { - global_nodes[num_global_nodes++] = gnode; + /* insert only if not yet found in table */ + if (gnode && *gnode != '\0' && nghash_find(glonodes, gnode) == NULL) { nghash_insert(glonodes, gnode, DUMMYDATA); } s = skip_ws(t); } c->line[0] = '*'; /* comment it out */ } - nghash_free(glonodes, NULL, NULL); + #ifdef TRACE { @@ -201,10 +187,7 @@ collect_global_nodes(struct card *c) static void free_global_nodes(void) { - int i; - for (i = 0; i < num_global_nodes; i++) - tfree(global_nodes[i]); - num_global_nodes = 0; + nghash_free(glonodes, NULL, NULL); } @@ -1124,14 +1107,19 @@ bxx_buffer(struct bxx_buffer *t) static void translate_node_name(struct bxx_buffer *buffer, const char *scname, const char *name, const char *name_e) { - const char *t; + bool isglobal; + if (!name_e) name_e = strchr(name, '\0'); - t = gettrans(name, name_e); + t = gettrans(name, name_e, &isglobal); + if (t) { bxx_put_cstring(buffer, t); + /* free only if t is global node, nodes from table[].t_new are freed elsewhere */ + if(isglobal) + tfree(t); } else { bxx_put_cstring(buffer, scname); bxx_putc(buffer, '.'); @@ -1641,21 +1629,26 @@ eq_substr(const char *str, const char *end, const char *cstring) * otherwise it returns NULL. *------------------------------------------------------------------------------*/ static char * -gettrans(const char *name, const char *name_end) +gettrans(const char *name, const char *name_end, bool *isglobal) { int i; + *isglobal = FALSE; if (!name_end) name_end = strchr(name, '\0'); /* Added by H.Tanaka to translate global nodes */ - for (i = 0; i Date: Fri, 17 Nov 2023 18:35:21 +0100 Subject: [PATCH 22/48] Generate seed numbers from a microseconds clock, not a seconds clock --- src/frontend/inp.c | 18 +++++++++++++++--- src/misc/win_time.c | 32 ++++++++++++++++++++++++++++++++ visualc/sharedspice.vcxproj | 1 + visualc/vngspice-fftw.vcxproj | 1 + visualc/vngspice.vcxproj | 1 + 5 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/misc/win_time.c diff --git a/src/frontend/inp.c b/src/frontend/inp.c index 228c63526..b226325de 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -89,6 +89,16 @@ extern void exec_controls(wordlist *controls); extern void SetSource(char *Name); #endif +#if defined (_MSC_VER) || defined (__MINGW32__) +typedef struct timeval { + long tv_sec; + long tv_usec; +} timeval; + +extern int gettimeofday(struct timeval* tp, void* unused); +#endif + + /* structure used to save expression parse trees for .model and * device instance lines */ @@ -101,6 +111,7 @@ struct pt_temper { struct pt_temper *next; }; + static int inp_parse_temper(struct card *deck, struct pt_temper **motdlist_p, struct pt_temper **devtlist_p); @@ -439,9 +450,10 @@ eval_opt(struct card* deck) char* token = gettok(&begtok); /* option seed=random [seed='random'] */ if (eq(token, "random") || eq(token, "{random}")) { - time_t acttime = time(NULL); - /* get random value from time in seconds since 1.1.1970 */ - int rseed = (int)(acttime - 1600000000); + struct timeval tv; + gettimeofday(&tv, NULL); + /* get random value from current timestamp microseconds */ + int rseed = (int)(tv.tv_usec); cp_vset("rndseed", CP_NUM, &rseed); com_sseed(NULL); has_seed = TRUE; diff --git a/src/misc/win_time.c b/src/misc/win_time.c new file mode 100644 index 000000000..fd1788d7e --- /dev/null +++ b/src/misc/win_time.c @@ -0,0 +1,32 @@ +#define WIN32_LEAN_AND_MEAN +//#include "ngspice/ngspice.h" +#include +#include +#include // portable: uint64_t MSVC: __int64 + +/*/ MSVC defines this in winsock2.h!? +typedef struct timeval { + long tv_sec; + long tv_usec; +} timeval; +*/ +int gettimeofday(struct timeval * tp, void * unused) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC) + // until 00:00:00 January 1, 1970 + static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime( &system_time ); + SystemTimeToFileTime( &system_time, &file_time ); + time = ((uint64_t)file_time.dwLowDateTime ) ; + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long) ((time - EPOCH) / 10000000L); + tp->tv_usec = (long) (system_time.wMilliseconds * 1000); + return 0; +} diff --git a/visualc/sharedspice.vcxproj b/visualc/sharedspice.vcxproj index 6ddb78d3c..e0dac4480 100644 --- a/visualc/sharedspice.vcxproj +++ b/visualc/sharedspice.vcxproj @@ -1220,6 +1220,7 @@ + diff --git a/visualc/vngspice-fftw.vcxproj b/visualc/vngspice-fftw.vcxproj index 27e22eff9..a5dd55c63 100644 --- a/visualc/vngspice-fftw.vcxproj +++ b/visualc/vngspice-fftw.vcxproj @@ -1685,6 +1685,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3 + diff --git a/visualc/vngspice.vcxproj b/visualc/vngspice.vcxproj index f366c96c3..279cc36d8 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -1700,6 +1700,7 @@ + From e57979d15d004fd05e61ce936215eab8cb8d291f Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 17 Nov 2023 23:50:59 +0100 Subject: [PATCH 23/48] Plug two memory leaks introduced by 2bbadc608 ("Use a hash table glonodes to prevent double or multiple entries in global node table global_nodes", 2023-11-17) --- src/frontend/subckt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c index 7da42f993..3bc81c46f 100644 --- a/src/frontend/subckt.c +++ b/src/frontend/subckt.c @@ -165,6 +165,7 @@ collect_global_nodes(struct card *c) if (gnode && *gnode != '\0' && nghash_find(glonodes, gnode) == NULL) { nghash_insert(glonodes, gnode, DUMMYDATA); } + tfree(gnode); s = skip_ws(t); } c->line[0] = '*'; /* comment it out */ @@ -1643,6 +1644,8 @@ gettrans(const char *name, const char *name_end, bool *isglobal) *isglobal = TRUE; return newgl; } + else + tfree(newgl); for (i = 0; table[i].t_old; i++) if (eq_substr(name, name_end, table[i].t_old)) { From 10756111bf00bc9c9fcbac6f85ab4f24e3787caa Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 18 Nov 2023 09:58:30 +0100 Subject: [PATCH 24/48] Fix a bug when deleting the memory. With the first call to mem_delete() all the hash table is deleted, a second call to mem_delete() does not have any further effect, so has to be avoided. --- src/spicelib/devices/cpl/cplhash.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/spicelib/devices/cpl/cplhash.c b/src/spicelib/devices/cpl/cplhash.c index 2aaeb5a15..ac2b81586 100644 --- a/src/spicelib/devices/cpl/cplhash.c +++ b/src/spicelib/devices/cpl/cplhash.c @@ -126,6 +126,8 @@ void my_key_free(void * key) void mem_delete(void) { #ifdef DB char buf[128]; + if (!memory_table) + return; printf("CPL GC memory allocated %d times, freed %d times\n", mem_in, mem_out); printf("CPL GC size of hash table to be freed: %d entries.\n", nghash_get_size(memory_table)); #ifdef DB_FULL @@ -142,6 +144,7 @@ void mem_delete(void) { #endif gc_is_on = 0; nghash_free(memory_table, NULL, my_key_free); + memory_table = NULL; #ifdef DB /* printf via sh_printf will need some info from variables that have been deleted already, therefore we use fputs */ From b62bb20e6f2842833e945c2fb4b839b59ba705bf Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 18 Nov 2023 10:55:19 +0100 Subject: [PATCH 25/48] Increase the allowable node name length from 24 to 256. Enable safe copying, no user induced buffer overflow. --- src/include/ngspice/swec.h | 2 +- src/spicelib/devices/cpl/cplsetup.c | 3 ++- src/spicelib/devices/txl/txlsetup.c | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/include/ngspice/swec.h b/src/include/ngspice/swec.h index 28c644fdd..e5ffe8720 100644 --- a/src/include/ngspice/swec.h +++ b/src/include/ngspice/swec.h @@ -184,7 +184,7 @@ typedef struct linked_lists_of_Bpoint{ } BPOINT, *BPOINTPTR; typedef struct linked_lists_of_nodeName{ - char id[24]; + char id[256]; struct linked_lists_of_nodeName *left, *right; NODE *nd; } NDname, *NDnamePt; diff --git a/src/spicelib/devices/cpl/cplsetup.c b/src/spicelib/devices/cpl/cplsetup.c index f80da3dd0..7e9052fb1 100644 --- a/src/spicelib/devices/cpl/cplsetup.c +++ b/src/spicelib/devices/cpl/cplsetup.c @@ -1934,7 +1934,8 @@ insert_ND(char* name, NDnamePt* ndn) memsaved(p); p->nd = NULL; p->right = p->left = NULL; - strcpy(p->id, name); + strncpy(p->id, name, 255); + p->id[255] = '\0'; return(p); } cmp = strcmp((*ndn)->id, name); diff --git a/src/spicelib/devices/txl/txlsetup.c b/src/spicelib/devices/txl/txlsetup.c index 01192d6d4..effc0be8b 100644 --- a/src/spicelib/devices/txl/txlsetup.c +++ b/src/spicelib/devices/txl/txlsetup.c @@ -1035,7 +1035,8 @@ insert_ND(char *name, NDnamePt *ndn) p = *ndn = TMALLOC(NDname, 1); p->nd = NULL; p->right = p->left = NULL; - strcpy(p->id, name); + strncpy(p->id, name, 255); + p->id[255] = '\0'; return(p); } cmp = strcmp((*ndn)->id, name); From e376c0e5c6ba81799897c21c64264707a3e7d3a4 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 18 Nov 2023 17:16:19 +0100 Subject: [PATCH 26/48] Eanble op or ac op (operating point), when selfheat is on. op for tran has been o.k. Fixme: preliminary fix or hack. --- src/spicelib/devices/vdmos/vdmosload.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/spicelib/devices/vdmos/vdmosload.c b/src/spicelib/devices/vdmos/vdmosload.c index 69fac79fd..3d27fcb30 100644 --- a/src/spicelib/devices/vdmos/vdmosload.c +++ b/src/spicelib/devices/vdmos/vdmosload.c @@ -84,6 +84,16 @@ VDMOSload(GENmodel *inModel, CKTcircuit *ckt) else Check_th = 0; + /* FIXME: + this is not a fix, but a hack: + with selfheat, op and op for ac don't work, NaN in self heating evalution of + first interation in CKTop(). Calling CKTop() from acan uses flag MODEDCOP, + changing this to MODETRANOP, as used by CKTop() called from dctran, op is o.k. + */ + if (selfheat) + if(ckt->CKTmode == 528) /* includes MODEDCOP */ + ckt->CKTmode = 544; /* includes MODETRANOP */ + /* first, we compute a few useful values - these could be * pre-computed, but for historical reasons are still done * here. They may be moved at the expense of instance size From 4d4f226a83dd15a3df165429fe6b3f17650a3103 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 18 Nov 2023 17:17:04 +0100 Subject: [PATCH 27/48] Prevent crash, when vector is defined, but still empty. --- src/frontend/com_measure2.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/frontend/com_measure2.c b/src/frontend/com_measure2.c index 5b947eacf..7033abbec 100644 --- a/src/frontend/com_measure2.c +++ b/src/frontend/com_measure2.c @@ -400,6 +400,11 @@ com_measure_when( return MEASUREMENT_FAILURE; } + if (dScale->v_realdata ==NULL && dScale->v_compdata == NULL) { + fprintf(cp_err, "Error: scale vector time, frequency or dc has no data.\n"); + return MEASUREMENT_FAILURE; + } + prevValue = 0.; prevValue2 = 0.; prevScaleValue = 0.; @@ -658,6 +663,11 @@ measure_at( return MEASUREMENT_FAILURE; } + if (dScale->v_realdata == NULL && dScale->v_compdata == NULL) { + fprintf(cp_err, "Error: scale vector time, frequency or dc has no data.\n"); + return MEASUREMENT_FAILURE; + } + /* ----------------------------------------------------------------- * Take the string tests outside of the loop for speed. * ----------------------------------------------------------------- */ @@ -768,7 +778,12 @@ measure_minMaxAvg( } if (dScale == NULL) { - fprintf(cp_err, "Error: no such vector as time, frquency or v-sweep.\n"); + fprintf(cp_err, "Error: no such vector as time, frequency or v-sweep.\n"); + return MEASUREMENT_FAILURE; + } + + if (dScale->v_realdata == NULL && dScale->v_compdata == NULL) { + fprintf(cp_err, "Error: scale vector time, frequency or v-sweep has no data.\n"); return MEASUREMENT_FAILURE; } @@ -944,7 +959,12 @@ measure_rms_integral( } if (xScale == NULL) { - fprintf(cp_err, "Error: no such vector as time.\n"); + fprintf(cp_err, "Error: no such vector as time, frequency or v-sweep.\n"); + return MEASUREMENT_FAILURE; + } + + if (xScale->v_realdata == NULL && xScale->v_compdata == NULL) { + fprintf(cp_err, "Error: scale vector time, frequency or v-sweep has no data.\n"); return MEASUREMENT_FAILURE; } From 86e991515ef28247380be5809d5c49897a32733e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 18 Nov 2023 20:09:49 +0100 Subject: [PATCH 28/48] Enable compiling with MINGW under MS Windows --- src/frontend/inp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index b226325de..73208f5c5 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -89,12 +89,13 @@ extern void exec_controls(wordlist *controls); extern void SetSource(char *Name); #endif -#if defined (_MSC_VER) || defined (__MINGW32__) +#if defined (_MSC_VER) typedef struct timeval { long tv_sec; long tv_usec; } timeval; - +#endif +#if defined (_MSC_VER) || defined (__MINGW32__) extern int gettimeofday(struct timeval* tp, void* unused); #endif From 40a783408c190df26b15093d34b86b37ab3cbe77 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 18 Nov 2023 20:10:17 +0100 Subject: [PATCH 29/48] Remove adms, enable klu --- compile_min_shared.sh | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/compile_min_shared.sh b/compile_min_shared.sh index ce8bc9565..547306116 100644 --- a/compile_min_shared.sh +++ b/compile_min_shared.sh @@ -10,10 +10,8 @@ # for debug version of shared ngspice # Options: -# --adms and --enable-adms will install extra HICUM, EKV and MEXTRAM models via the -# adms interface. # Please see http://ngspice.sourceforge.net/admshowto.html for more info on adms. -# CIDER, XSPICE, and OpenMP may be selected at will. +# CIDER, XSPICE, KLU, and OpenMP may be selected at will. # --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info. # To obtain a 32 bit executable, replace -m64 by -m32 ./configure lines. @@ -38,27 +36,19 @@ fi ./autogen.sh if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi -# Alternatively, if compiling sources from git, and want to add adms created devices, -# you may need to uncomment the following two lines (and don't forget to add adms option -# to the ../configure statement): -#./autogen.sh --adms -#if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi - echo if test "$1" = "d"; then cd debug-sh if [ $? -ne 0 ]; then echo "cd debug-sh failed"; exit 1 ; fi echo "configuring for 64 bit debug" echo -# You may add --enable-adms to the following command for adding adms generated devices - ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-relpath --disable-debug prefix="C:/Spice64d" CFLAGS="-m64 -g -O0 -Wall" LDFLAGS="-m64" + ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --enable-relpath prefix="C:/Spice64d" CFLAGS="-m64 -g -O0 -Wall" LDFLAGS="-m64" else cd release-sh if [ $? -ne 0 ]; then echo "cd release-sh failed"; exit 1 ; fi echo "configuring for 64 bit release" echo -# You may add --enable-adms to the following command for adding adms generated devices - ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-relpath --disable-debug prefix="C:/Spice64" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" + ../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --enable-relpath --disable-debug prefix="C:/Spice64" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" fi if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi From ecf4f819dcb142ff1d1db35e1707047b50289aeb Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 19 Nov 2023 10:44:42 +0100 Subject: [PATCH 30/48] typo --- src/spicelib/devices/vdmos/vdmosload.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/spicelib/devices/vdmos/vdmosload.c b/src/spicelib/devices/vdmos/vdmosload.c index 3d27fcb30..644487245 100644 --- a/src/spicelib/devices/vdmos/vdmosload.c +++ b/src/spicelib/devices/vdmos/vdmosload.c @@ -86,9 +86,9 @@ VDMOSload(GENmodel *inModel, CKTcircuit *ckt) /* FIXME: this is not a fix, but a hack: - with selfheat, op and op for ac don't work, NaN in self heating evalution of - first interation in CKTop(). Calling CKTop() from acan uses flag MODEDCOP, - changing this to MODETRANOP, as used by CKTop() called from dctran, op is o.k. + with selfheat, op and op for ac don't work, NaN in self heating evaluation of + first iteration in CKTop(). Calling CKTop() from acan uses flag MODEDCOP, + changing this to MODETRANOP, as used by CKTop() called from dctran, then op is o.k. */ if (selfheat) if(ckt->CKTmode == 528) /* includes MODEDCOP */ From 1db8eff25c06f3f909ee75ca658c971145835fac Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Thu, 2 Nov 2023 15:30:54 +0100 Subject: [PATCH 31/48] Write message only once. --- src/frontend/inpcom.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 0993e1712..987aeddbf 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -8351,6 +8351,7 @@ static void inp_check_syntax(struct card *deck) bool mwarn = FALSE; char* subs[10]; /* store subckt lines */ int ends = 0; /* store .ends line numbers */ + static bool nesting_once = TRUE; /* prevent crash in inp.c, fcn inp_spsource: */ if (ciprefix(".param", deck->line) || ciprefix(".meas", deck->line)) { @@ -8423,10 +8424,12 @@ static void inp_check_syntax(struct card *deck) } } // nesting may be critical if params are involved - if (check_subs > 0 && strchr(cut_line, '=')) + if (nesting_once && check_subs > 0 && strchr(cut_line, '=')) { fprintf(cp_err, - "\nWarning: Nesting of subcircuits with parameters " - "is only marginally supported!\n\n"); + "\nWarning: Nesting of subcircuits with parameters " + "is only marginally supported!\n\n"); + nesting_once = FALSE; + } if (check_subs < 10) subs[check_subs] = cut_line; else From 3619041e260f5ce0aece5e68b81187acc75bf13d Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Thu, 2 Nov 2023 15:31:25 +0100 Subject: [PATCH 32/48] Add new lock for realloc of vectors --- src/frontend/dvec.c | 56 +++++++++++++++++++++++++++++++++++++++++++-- src/sharedspice.c | 1 + 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/frontend/dvec.c b/src/frontend/dvec.c index 5bc7b5df6..41177df01 100644 --- a/src/frontend/dvec.c +++ b/src/frontend/dvec.c @@ -1,6 +1,51 @@ + + +#if defined(__MINGW32__) || defined(_MSC_VER) +#include +#endif + #include "ngspice/ngspice.h" #include "ngspice/dvec.h" +/*Use Windows threads if on W32 without pthreads*/ +#ifndef HAVE_LIBPTHREAD + +#if defined(__MINGW32__) || defined(_MSC_VER) +//#if defined(_MSC_VER) +#ifdef SRW +#define mutex_lock(a) AcquireSRWLockExclusive(a) +#define mutex_unlock(a) ReleaseSRWLockExclusive(a) +typedef SRWLOCK mutexType; +#else +#define mutex_lock(a) EnterCriticalSection(a) +#define mutex_unlock(a) LeaveCriticalSection(a) +typedef CRITICAL_SECTION mutexType; +#endif +#define thread_self() GetCurrentThread() +#define threadid_self() GetCurrentThreadId() +typedef HANDLE threadId_t; +#define WIN_THREADS +#define THREADS + +#endif + +#else + +#include +#define mutex_lock(a) pthread_mutex_lock(a) +#define mutex_unlock(a) pthread_mutex_unlock(a) +#define thread_self() pthread_self() +#define threadid_self() 0 //FIXME t.b.d. +typedef pthread_mutex_t mutexType; +typedef pthread_t threadId_t; +#define THREADS +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static bool cont_condition; + +#endif + +extern mutexType vecreallocMutex; + struct dvec *dvec_alloc(/* NOT const -- assigned to char */ char *name, int type, short flags, int length, void *storage) @@ -86,17 +131,24 @@ void dvec_realloc(struct dvec *v, int length, void *storage) v->v_alloc_length = length; } /* end of function dvec_realloc */ - +/* called from plotAddReal(Complex)Value, to increase + storage for result vectors. Locking and unlocking + is done by API functions ng_veclock(), ng_vecunlock(). */ void dvec_extend(struct dvec *v, int length) { +#if defined SHARED_MODULE + mutex_lock(&vecreallocMutex); +#endif if (isreal(v)) { v->v_realdata = TREALLOC(double, v->v_realdata, length); } else { v->v_compdata = TREALLOC(ngcomplex_t, v->v_compdata, length); } - v->v_alloc_length = length; +#if defined SHARED_MODULE + mutex_unlock(&vecreallocMutex); +#endif } /* end of function dvec_extend */ diff --git a/src/sharedspice.c b/src/sharedspice.c index 2685e3686..10145c845 100644 --- a/src/sharedspice.c +++ b/src/sharedspice.c @@ -396,6 +396,7 @@ unsigned int main_id, ng_id, command_id; mutexType triggerMutex; mutexType allocMutex; mutexType fputsMutex; +mutexType vecreallocMutex; #endif /* initialization status */ From e87651e27fd3ead872c06fdbe06585f8154f68c2 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 19 Nov 2023 11:32:25 +0100 Subject: [PATCH 33/48] Update to comments, restrict mutex definitions to shared ngspice --- src/frontend/dvec.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/frontend/dvec.c b/src/frontend/dvec.c index 41177df01..71e45c125 100644 --- a/src/frontend/dvec.c +++ b/src/frontend/dvec.c @@ -7,6 +7,8 @@ #include "ngspice/ngspice.h" #include "ngspice/dvec.h" +#if defined SHARED_MODULE + /*Use Windows threads if on W32 without pthreads*/ #ifndef HAVE_LIBPTHREAD @@ -46,6 +48,7 @@ static bool cont_condition; extern mutexType vecreallocMutex; +#endif struct dvec *dvec_alloc(/* NOT const -- assigned to char */ char *name, int type, short flags, int length, void *storage) @@ -103,7 +106,7 @@ struct dvec *dvec_alloc(/* NOT const -- assigned to char */ char *name, } /* end of function dvec_alloc */ -/* Resize dvec to length if storage is NULL orr replace +/* Resize dvec to length if storage is NULL or replace * its existing allocation with storage if not */ void dvec_realloc(struct dvec *v, int length, void *storage) @@ -132,8 +135,10 @@ void dvec_realloc(struct dvec *v, int length, void *storage) } /* end of function dvec_realloc */ /* called from plotAddReal(Complex)Value, to increase - storage for result vectors. Locking and unlocking - is done by API functions ng_veclock(), ng_vecunlock(). */ + storage for result vectors. + In shared ngspice this may be locked, e.g. during plotting in the primary + thread, while the simulation is running in the background thread. Locking and unlocking + is done by API functions ngSpice_LockRealloc(), ngSpice_UnlockRealloc(). */ void dvec_extend(struct dvec *v, int length) { #if defined SHARED_MODULE From 1849a8d220aaf592f985dfa87f91fafd1a4c8960 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 19 Nov 2023 11:33:56 +0100 Subject: [PATCH 34/48] Add functions ngSpice_LockRealloc and ngSpice_UnlockRealloc to lock reallocing output vectors, e.g. during reading the vecs for plotting. --- src/sharedspice.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sharedspice.c b/src/sharedspice.c index 10145c845..1563e040f 100644 --- a/src/sharedspice.c +++ b/src/sharedspice.c @@ -775,6 +775,8 @@ read_initialisation_file(const char *dir, const char *name) /* The functions exported explicitely from shared ngspice */ /**********************************************************/ + + #ifdef THREADS /* Checks if ngspice is running in the background */ @@ -1337,6 +1339,20 @@ char** ngSpice_AllEvtNodes(void) } #endif +/* Lock/unlock realloc of result vectors during plotting */ +IMPEXP +int ngSpice_LockRealloc(void) +{ + mutex_lock(&vecreallocMutex); + return 1; +} + +IMPEXP +int ngSpice_UnlockRealloc(void) +{ + mutex_unlock(&vecreallocMutex); + return 1; +} /* add the preliminary breakpoints to the list. called from dctran.c */ From dd58b07949830a9143e6f9b3ce173a207d128544 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 19 Nov 2023 12:37:29 +0100 Subject: [PATCH 35/48] Initialize the new mutex vecreallocMutex --- src/sharedspice.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sharedspice.c b/src/sharedspice.c index 1563e040f..523f1c2bf 100644 --- a/src/sharedspice.c +++ b/src/sharedspice.c @@ -860,16 +860,19 @@ ngSpice_Init(SendChar* printfcn, SendStat* statusfcn, ControlledExit* ngspiceexi pthread_mutex_init(&triggerMutex, NULL); pthread_mutex_init(&allocMutex, NULL); pthread_mutex_init(&fputsMutex, NULL); + pthread_mutex_init(&vecreallocMutex, NULL); cont_condition = FALSE; #else #ifdef SRW InitializeSRWLock(&triggerMutex); InitializeSRWLock(&allocMutex); InitializeSRWLock(&fputsMutex); + InitializeSRWLock(&vecreallocMutex); #else InitializeCriticalSection(&triggerMutex); InitializeCriticalSection(&allocMutex); InitializeCriticalSection(&fputsMutex); + InitializeCriticalSection(&vecreallocMutex); #endif #endif // Id of primary thread From 3d1cbf93eed0bcf73c51c2e8e9bc0801e1eb1a8f Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Thu, 23 Nov 2023 18:29:48 +0100 Subject: [PATCH 36/48] Add a predifined variable 'skywaterpdk' to speed up circuit loading and parsing. If set, it removes quoting tokens for params. This requires that Skywater PDK does adequately stick to the ngspice syntax to put params in between braces. As far as tests go, it does. It also set the number of nodes for MSO devices to 4, so no lengthy search is required. --- src/frontend/inpcom.c | 3 +++ src/frontend/options.c | 4 +++- src/frontend/subckt.c | 3 +++ src/include/ngspice/fteext.h | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 987aeddbf..710b2c1f0 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -7860,6 +7860,9 @@ static void inp_quote_params(struct card *c, struct card *end_c, { bool in_control = FALSE; + if (ft_skywaterpdk) + return; + for (; c && c != end_c; c = c->nextcard) { int i, j, num_terminals; diff --git a/src/frontend/options.c b/src/frontend/options.c index d246ad318..ac12dc0b3 100644 --- a/src/frontend/options.c +++ b/src/frontend/options.c @@ -23,7 +23,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group bool ft_acctprint = FALSE, ft_noacctprint = FALSE, ft_listprint = FALSE; bool ft_nodesprint = FALSE, ft_optsprint = FALSE, ft_noinitprint = FALSE; -bool ft_norefprint = FALSE; +bool ft_norefprint = FALSE, ft_skywaterpdk = FALSE; bool ft_ngdebug = FALSE, ft_nginfo = FALSE, ft_stricterror = FALSE; static void setdb(char *str); @@ -307,6 +307,8 @@ cp_usrset(struct variable *var, bool isset) ft_ngdebug = isset; } else if (eq(var->va_name, "nginfo")) { ft_nginfo = isset; + } else if (eq(var->va_name, "skywaterpdk")) { + ft_skywaterpdk = isset; } else if (eq(var->va_name, "noinit")) { ft_noinitprint = isset; } else if (eq(var->va_name, "norefvalue")) { diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c index 3bc81c46f..d72a4ef90 100644 --- a/src/frontend/subckt.c +++ b/src/frontend/subckt.c @@ -1695,6 +1695,9 @@ numnodes(const char *line, struct subs *subs, wordlist const *modnames) return (nodes); } } + /* if we use option skywaterpdk, MOS has four nodes. Required if number of devices is large */ + if (ft_skywaterpdk && c == 'm') + return 4; n = inp_numnodes(c); diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 9f6848503..589c62564 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -268,6 +268,7 @@ extern struct card *inp_getoptsc(char *line, struct card *options); extern bool ft_ngdebug; extern bool ft_nginfo; extern bool ft_stricterror; +extern bool ft_skywaterpdk; /* parse.c */ From 508fad0f55dcda3545305d6582c367ef7dca06ac Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Tue, 10 Oct 2023 21:44:03 +0100 Subject: [PATCH 37/48] In dctran.c, call EVTcall_hybrids() (which then calls a subset of code model instances) only when the current timestep was successful. That is efficient and means that code models may rely on such calls, for example a co-simulator may safely advance to the end of the timestep. The function cm_irreversible() that is mentioned in a comment will be added in a later commit. --- src/spicelib/analysis/dctran.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/spicelib/analysis/dctran.c b/src/spicelib/analysis/dctran.c index 3a913cefc..e7da2097c 100644 --- a/src/spicelib/analysis/dctran.c +++ b/src/spicelib/analysis/dctran.c @@ -787,14 +787,6 @@ resume: converged = NIiter(ckt,ckt->CKTtranMaxIter); -#ifdef XSPICE - if(ckt->evt->counts.num_insts > 0) { - g_mif_info.circuit.evt_step = ckt->CKTtime; - EVTcall_hybrids(ckt); - } -/* gtri - end - wbk - Call all hybrids */ - -#endif ckt->CKTstat->STATtimePts ++; ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITPRED; if(firsttime) { @@ -830,8 +822,10 @@ resume: #ifdef XSPICE /* gtri - begin - wbk - Add Breakpoint stuff */ - /* Force backup if temporary breakpoint is < current time */ } else if(g_mif_info.breakpoint.current < ckt->CKTtime) { + /* Force backup if temporary breakpoint is < current time */ + + past_breakpoint: ckt->CKTsaveDelta = ckt->CKTdelta; ckt->CKTtime -= ckt->CKTdelta; ckt->CKTdelta = g_mif_info.breakpoint.current - ckt->CKTtime; @@ -879,6 +873,27 @@ resume: return(error); } if (newdelta > .9 * ckt->CKTdelta) { +#if defined(XSPICE) + /* The timestep has succeeded. XSPICE instances with + * both analog and event ports ("hybrids") and others + * that have called cm_irreversible() receive an EVENT + * call here that allows them to capture their final + * port values and advance co-simulations. As this is an EVENT + * call, they are not expected to do any integrations, + * so there is no need for a further convergence test. + */ + + if (ckt->evt->counts.num_hybrids > 0) { + g_mif_info.circuit.evt_step = ckt->CKTtime; + EVTcall_hybrids(ckt); + if (g_mif_info.breakpoint.current < ckt->CKTtime) { + /* A hybrid requested a breakpoint in the past. */ + + goto past_breakpoint; + } + } +#endif + if ((ckt->CKTorder == 1) && (ckt->CKTmaxOrder > 1)) { /* don't rise the order for backward Euler */ newdelta = ckt->CKTdelta; ckt->CKTorder = 2; @@ -893,6 +908,7 @@ resume: } /* time point OK - 630 */ ckt->CKTdelta = newdelta; + #ifdef NDEV if (!ft_norefprint) { /* show a time process indicator, by Gong Ding, gdiso@ustc.edu */ From 34e6c4abea23138a107148f36edb609b645f35d0 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 11 Oct 2023 17:06:23 +0100 Subject: [PATCH 38/48] Convert XSPICE's hybrid_index array to instance pointers. That simplifies code and will be needed for irreversible code model support. --- src/include/ngspice/evt.h | 28 ++++++++++------------------ src/include/ngspice/evtproto.h | 2 +- src/xspice/evt/evtcall_hybrids.c | 13 ++++++------- src/xspice/evt/evtdest.c | 2 +- src/xspice/evt/evtinit.c | 11 +++++------ src/xspice/evt/evtiter.c | 2 +- src/xspice/evt/evtload.c | 12 +++++------- 7 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/include/ngspice/evt.h b/src/include/ngspice/evt.h index 7dfb35c27..0f01779d5 100644 --- a/src/include/ngspice/evt.h +++ b/src/include/ngspice/evt.h @@ -91,35 +91,27 @@ struct Evt_Node_Info { }; struct Evt_Inst_Info { - Evt_Inst_Info_t *next; /* the next in the linked list of node info */ + Evt_Inst_Info_t *next; /* the next in the linked list */ MIFinstance *inst_ptr; /* Pointer to MIFinstance struct for this instance */ }; struct Evt_Info { - Evt_Inst_Info_t *inst_list; /* static info about event/hybrid instances */ - Evt_Node_Info_t *node_list; /* static info about event nodes */ - Evt_Port_Info_t *port_list; /* static info about event ports */ - Evt_Output_Info_t *output_list; /* static info about event outputs */ - int *hybrid_index; /* vector of inst indexs for hybrids */ - Evt_Inst_Info_t **inst_table; /* vector of pointers to elements in inst_list */ - Evt_Node_Info_t **node_table; /* vector of pointers to elements in node_list */ - Evt_Port_Info_t **port_table; /* vector of pointers to elements in port_list */ - Evt_Output_Info_t **output_table; /* vector of pointers to elements in output_list */ + Evt_Inst_Info_t *inst_list; /* static info about event instances */ + Evt_Node_Info_t *node_list; /* static info about event nodes */ + Evt_Port_Info_t *port_list; /* static info about event ports */ + Evt_Output_Info_t *output_list; /* static info about event outputs */ + MIFinstance **hybrids; /* vector of inst pointers for hybrids */ + Evt_Inst_Info_t **inst_table; /* vector of pointers to elements in inst_list */ + Evt_Node_Info_t **node_table; /* vector of pointers to elements in node_list */ + Evt_Port_Info_t **port_table; /* vector of pointers to elements in port_list */ + Evt_Output_Info_t **output_table; /* vector of pointers to elements in output_list */ }; - - - - - - /* *************** */ /* Queue structure */ /* *************** */ - - struct Evt_Inst_Event { Evt_Inst_Event_t *next; /* the next in the linked list */ double event_time; /* Time for this event to happen */ diff --git a/src/include/ngspice/evtproto.h b/src/include/ngspice/evtproto.h index f37bb0fa7..83066409b 100644 --- a/src/include/ngspice/evtproto.h +++ b/src/include/ngspice/evtproto.h @@ -92,7 +92,7 @@ void EVTqueue_inst( void EVTdequeue(CKTcircuit *ckt, double time); -int EVTload(CKTcircuit *ckt, int inst_index); +int EVTload(CKTcircuit *ckt, MIFinstance *inst); void EVTprint(wordlist *wl); void EVTprintvcd(wordlist *wl); diff --git a/src/xspice/evt/evtcall_hybrids.c b/src/xspice/evt/evtcall_hybrids.c index f8a82106c..1427b746d 100644 --- a/src/xspice/evt/evtcall_hybrids.c +++ b/src/xspice/evt/evtcall_hybrids.c @@ -60,18 +60,17 @@ void EVTcall_hybrids( CKTcircuit *ckt) /* the main circuit structure */ { - int i; - int num_hybrids; - - int *hybrid_index; + int i; + int num_hybrids; + MIFinstance **hybrids; /* Get needed data for fast access */ + num_hybrids = ckt->evt->counts.num_hybrids; - hybrid_index = ckt->evt->info.hybrid_index; + hybrids = ckt->evt->info.hybrids; /* Call EVTload for all hybrids */ for(i = 0; i < num_hybrids; i++) - EVTload(ckt, hybrid_index[i]); - + EVTload(ckt, hybrids[i]); } diff --git a/src/xspice/evt/evtdest.c b/src/xspice/evt/evtdest.c index c80367686..4fa4dfca9 100644 --- a/src/xspice/evt/evtdest.c +++ b/src/xspice/evt/evtdest.c @@ -330,5 +330,5 @@ Evt_Info_destroy(Evt_Info_t *info) } tfree(info->output_table); - tfree(info->hybrid_index); + tfree(info->hybrids); } diff --git a/src/xspice/evt/evtinit.c b/src/xspice/evt/evtinit.c index 283c1ea79..17883d8b1 100644 --- a/src/xspice/evt/evtinit.c +++ b/src/xspice/evt/evtinit.c @@ -207,7 +207,7 @@ static int EVTinit_info( Evt_Port_Info_t **port_table = NULL; Evt_Output_Info_t **output_table = NULL; - int *hybrid_index = NULL; + MIFinstance **hybrids = NULL; int num_hybrids; @@ -253,15 +253,14 @@ static int EVTinit_info( ckt->evt->info.output_table = output_table; - /* Allocate and create table of indexes into inst_table for hybrids */ + /* Allocate and create table of hybrids */ num_hybrids = ckt->evt->counts.num_hybrids; - CKALLOC(hybrid_index, num_hybrids, int) + CKALLOC(hybrids, num_hybrids, MIFinstance *) for(i = 0, j = 0; i < num_insts; i++) { if(inst_table[i]->inst_ptr->analog) - hybrid_index[j++] = i; + hybrids[j++] = inst_table[i]->inst_ptr; } - ckt->evt->info.hybrid_index = hybrid_index; - + ckt->evt->info.hybrids = hybrids; /* Return */ return(OK); diff --git a/src/xspice/evt/evtiter.c b/src/xspice/evt/evtiter.c index 7f906a355..f72573a5f 100644 --- a/src/xspice/evt/evtiter.c +++ b/src/xspice/evt/evtiter.c @@ -256,7 +256,7 @@ int EVTiter( for(i = 0; i < num_to_call; i++) { inst_index = inst_queue->to_call_index[i]; inst_queue->to_call[inst_index] = MIF_FALSE; - EVTload(ckt, inst_index); + EVTload(ckt, ckt->evt->info.inst_table[inst_index]->inst_ptr); } inst_queue->num_to_call = 0; diff --git a/src/xspice/evt/evtload.c b/src/xspice/evt/evtload.c index a2603e818..edce5e119 100644 --- a/src/xspice/evt/evtload.c +++ b/src/xspice/evt/evtload.c @@ -82,8 +82,8 @@ ignored. */ int EVTload( - CKTcircuit *ckt, /* The circuit structure */ - int inst_index) /* The instance to call code model for */ + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *inst) /* The instance to call */ { int i; @@ -96,10 +96,9 @@ int EVTload( Mif_Conn_Data_t *conn; Mif_Port_Data_t *port; Evt_Node_Data_t *node_data; - MIFinstance *inst; Mif_Private_t cm_data; - + void *value_ptr; /* ***************************** */ /* Prepare the code model inputs */ @@ -107,7 +106,7 @@ int EVTload( /* Get pointer to instance data structure and other data */ /* needed for fast access */ - inst = ckt->evt->info.inst_table[inst_index]->inst_ptr; + node_data = ckt->evt->data.node; /* Setup circuit data in struct to be passed to code model function */ @@ -145,8 +144,7 @@ int EVTload( /* create a new state for the instance */ if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized) - EVTcreate_state(ckt, inst_index); - + EVTcreate_state(ckt, inst->inst_index); /* Loop through all connections on the instance and setup */ /* load, total_load, and msg on all ports, and changed flag */ From 12fe7b90c469e56ea494d00877e8c182ad8db941 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 18 Oct 2023 09:16:39 +0100 Subject: [PATCH 39/48] Add new code model function cm_irreversible(). This may only be called from an XSPICE code model's INIT call and has three effects: the instance is treated as "hybrid" even when it does not use both port types; its position in the hybrids array is adusted; and it is explicitly notifed when called just befoore the end of an analog time-step. --- src/include/ngspice/cmconstants.h | 1 + src/include/ngspice/cmproto.h | 1 + src/include/ngspice/dllitf.h | 1 + src/include/ngspice/evtproto.h | 2 + src/include/ngspice/mifdefs.h | 1 + src/include/ngspice/miftypes.h | 1 + src/xspice/cm/cm.c | 96 +++++++++++++++++++++++++++++++ src/xspice/cm/cmexport.c | 1 + src/xspice/evt/evtcall_hybrids.c | 9 ++- src/xspice/evt/evtload.c | 28 +++++++-- src/xspice/icm/dlmain.c | 5 ++ 11 files changed, 138 insertions(+), 8 deletions(-) diff --git a/src/include/ngspice/cmconstants.h b/src/include/ngspice/cmconstants.h index abfd45fa4..73563e14f 100644 --- a/src/include/ngspice/cmconstants.h +++ b/src/include/ngspice/cmconstants.h @@ -52,6 +52,7 @@ NON-STANDARD FEATURES #define ANALOG MIF_ANALOG #define EVENT MIF_EVENT_DRIVEN +#define STEP_PENDING MIF_STEP_PENDING #endif diff --git a/src/include/ngspice/cmproto.h b/src/include/ngspice/cmproto.h index 7ab6ffbd5..17518160c 100644 --- a/src/include/ngspice/cmproto.h +++ b/src/include/ngspice/cmproto.h @@ -97,6 +97,7 @@ int cm_message_printf(const char *fmt, ...); double cm_netlist_get_c(void); double cm_netlist_get_l(void); +void cm_irreversible(unsigned int); const char *cm_get_node_name(const char *, unsigned int); bool cm_probe_node(unsigned int, unsigned int, void *); bool cm_schedule_output(unsigned int, unsigned int, double, void *); diff --git a/src/include/ngspice/dllitf.h b/src/include/ngspice/dllitf.h index 1d9295d81..eaeef3d77 100644 --- a/src/include/ngspice/dllitf.h +++ b/src/include/ngspice/dllitf.h @@ -59,6 +59,7 @@ struct coreInfo_t { int ((*dllitf_cm_message_send)(char *)); double ((*dllitf_cm_netlist_get_c)(void)); double ((*dllitf_cm_netlist_get_l)(void)); + void ((*dllitf_cm_irreversible)(unsigned int)); const char * ((*dllitf_cm_get_node_name)(const char *, unsigned int)); bool ((*dllitf_cm_probe_node)(unsigned int, unsigned int, void *)); diff --git a/src/include/ngspice/evtproto.h b/src/include/ngspice/evtproto.h index 83066409b..ea2bd5f92 100644 --- a/src/include/ngspice/evtproto.h +++ b/src/include/ngspice/evtproto.h @@ -94,6 +94,8 @@ void EVTdequeue(CKTcircuit *ckt, double time); int EVTload(CKTcircuit *ckt, MIFinstance *inst); +int EVTload_with_event(CKTcircuit *ckt, MIFinstance *inst, Mif_Call_Type_t type); + void EVTprint(wordlist *wl); void EVTprintvcd(wordlist *wl); void EVTsave(wordlist *wl); diff --git a/src/include/ngspice/mifdefs.h b/src/include/ngspice/mifdefs.h index 801166f44..c3564e754 100644 --- a/src/include/ngspice/mifdefs.h +++ b/src/include/ngspice/mifdefs.h @@ -80,6 +80,7 @@ struct MIFinstance { Mif_Boolean_t analog; /* true if this inst is analog or hybrid type */ Mif_Boolean_t event_driven; /* true if this inst is event-driven or hybrid type */ + unsigned int irreversible; /* non-zero for special treatment */ int inst_index; /* Index into inst_table in evt struct in ckt */ Mif_Callback_t callback; /* instance callback function */ diff --git a/src/include/ngspice/miftypes.h b/src/include/ngspice/miftypes.h index aa8c6f4b5..532b884e1 100644 --- a/src/include/ngspice/miftypes.h +++ b/src/include/ngspice/miftypes.h @@ -75,6 +75,7 @@ typedef enum { typedef enum { MIF_ANALOG, /* Analog call */ MIF_EVENT_DRIVEN, /* Event-driven call */ + MIF_STEP_PENDING, /* Special event call for irreversible Code Models */ } Mif_Call_Type_t; diff --git a/src/xspice/cm/cm.c b/src/xspice/cm/cm.c index 61ba08819..8a3a283e6 100644 --- a/src/xspice/cm/cm.c +++ b/src/xspice/cm/cm.c @@ -38,6 +38,7 @@ INTERFACES cm_get_path() cm_get_circuit() + cm_irreversible() cm_get_node_name() cm_probe_node() @@ -57,6 +58,7 @@ NON-STANDARD FEATURES #include "ngspice/enh.h" #include "ngspice/mif.h" #include "ngspice/cktdefs.h" +#include "ngspice/cpextern.h" //#include "util.h" @@ -730,6 +732,100 @@ CKTcircuit *cm_get_circuit(void) return(g_mif_info.ckt); } +/* Set the "irreversible" flag on the current instance and shuffle it to the + * requested position among any other irreversibles in the hybrid_index array. + * Array entries are sorted so that non-zero values of instance->irreversible + * are decreasing: an instance with instance->irreversible == 1 is fully + * protected. + */ + +static void duplicate(MIFinstance *instance) +{ + fprintf(cp_err, + "Warning: Duplicate value %d in cm_irreversible() " + "for instance %s.\n", + instance->irreversible, instance->gen.GENname); +} + +void cm_irreversible(unsigned int place) +{ + MIFinstance *instance; + Evt_Ckt_Data_t *evt; + int num_hybrids; + MIFinstance **hybrids; + int old_index, i; + unsigned int value; + + instance = g_mif_info.instance; + if (!g_mif_info.circuit.init) { + fprintf(cp_err, + "%s: Ignoring call to cm_irreversible(): not in INIT\n", + instance->gen.GENname); + return; + } + if (instance->irreversible || place == 0) { + if (instance->irreversible != place) { + fprintf(cp_err, "%s: Ignoring new value %d in cm_irreversible()\n", + instance->gen.GENname, place); + } + return; + } + instance->irreversible = place; + + evt = g_mif_info.ckt->evt; + num_hybrids = evt->counts.num_hybrids; + hybrids = evt->info.hybrids; + + /* Already a hybrid? */ + + for (old_index = 0; old_index < num_hybrids; ++old_index) { + if (hybrids[old_index] == instance) + break; + } + + if (old_index < num_hybrids) { + /* Existing hybrid, move down, shuffling other entries up. */ + + for (i = old_index + 1; i < num_hybrids; ++i) { + value = hybrids[i]->irreversible; + if (value == 0 || value > place) { + hybrids[i - 1] = hybrids[i]; + } else if (value == place) { + duplicate(instance); + break; + } else { + break; + } + } + hybrids[i - 1] = instance; + } else { + /* Instance is not hybrid, add an entry. */ + + num_hybrids++; + hybrids = TREALLOC(MIFinstance *, hybrids, num_hybrids); + evt->counts.num_hybrids = num_hybrids; + evt->info.hybrids = hybrids; + if (hybrids == NULL) { + fprintf(cp_err, "Allocation failed in cm_irreversible()\n"); + abort(); + } + + /* Shuffle entries down. */ + + for (i = num_hybrids - 2; i >= 0; --i) { + value = hybrids[i]->irreversible; + if (value != 0 && value < place) { + hybrids[i + 1] = hybrids[i]; + } else if (value == place) { + duplicate(instance); + } else { + break; + } + } + hybrids[i + 1] = instance; + } +} + /* Get the name of a circuit node connected to a port. */ const char *cm_get_node_name(const char *port_name, unsigned int index) diff --git a/src/xspice/cm/cmexport.c b/src/xspice/cm/cmexport.c index a644cb921..eb7846363 100644 --- a/src/xspice/cm/cmexport.c +++ b/src/xspice/cm/cmexport.c @@ -58,6 +58,7 @@ struct coreInfo_t coreInfo = cm_message_send, cm_netlist_get_c, cm_netlist_get_l, + cm_irreversible, cm_get_node_name, cm_probe_node, cm_schedule_output, diff --git a/src/xspice/evt/evtcall_hybrids.c b/src/xspice/evt/evtcall_hybrids.c index 1427b746d..a45f2506e 100644 --- a/src/xspice/evt/evtcall_hybrids.c +++ b/src/xspice/evt/evtcall_hybrids.c @@ -20,10 +20,12 @@ MODIFICATIONS SUMMARY This file contains function EVTcall_hybrids which calls all models - which have both analog and event-driven ports. It is called following + which have both analog and event-driven ports or have declared + themselves to be irreversible (no back-out). It is called following successful evaluation of an analog iteration attempt to allow events to be scheduled by the hybrid models. The 'CALL_TYPE' is set - to 'EVENT_DRIVEN' when the model is called from this function. + to 'EVENT_DRIVEN' or 'STEP_PENDING' when the model is called + from this function. INTERFACES @@ -71,6 +73,7 @@ void EVTcall_hybrids( hybrids = ckt->evt->info.hybrids; /* Call EVTload for all hybrids */ + for(i = 0; i < num_hybrids; i++) - EVTload(ckt, hybrids[i]); + EVTload_with_event(ckt, hybrids[i], MIF_STEP_PENDING); } diff --git a/src/xspice/evt/evtload.c b/src/xspice/evt/evtload.c index edce5e119..a0b75326a 100644 --- a/src/xspice/evt/evtload.c +++ b/src/xspice/evt/evtload.c @@ -85,6 +85,16 @@ int EVTload( CKTcircuit *ckt, /* The circuit structure */ MIFinstance *inst) /* The instance to call */ { + return EVTload_with_event(ckt, inst, MIF_EVENT_DRIVEN); +} + +/* "Internal" version, also used by EVTcall_hybrids(). */ + +int EVTload_with_event( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *inst, /* The instance to call */ + Mif_Call_Type_t type) /* Type of call (EVENT or STEP_PENDING). */ +{ int i; int j; @@ -104,8 +114,7 @@ int EVTload( /* Prepare the code model inputs */ /* ***************************** */ - /* Get pointer to instance data structure and other data */ - /* needed for fast access */ + /* Get pointer to data structure needed for fast access */ node_data = ckt->evt->data.node; @@ -124,7 +133,14 @@ int EVTload( else cm_data.circuit.time = 0.0; - cm_data.circuit.call_type = MIF_EVENT_DRIVEN; + /* Instances that have declared themselves as irreversible + * are expected to distinguish STEP_PENDING from ordinary events. + */ + + if (type == MIF_STEP_PENDING && inst->irreversible) + cm_data.circuit.call_type = MIF_STEP_PENDING; + else + cm_data.circuit.call_type = MIF_EVENT_DRIVEN; cm_data.circuit.temperature = ckt->CKTtemp - 273.15; /* Setup data needed by cm_... functions */ @@ -141,9 +157,11 @@ int EVTload( /* If after initialization and in transient analysis mode */ - /* create a new state for the instance */ + /* create a new state for the instance, */ + /* except analog-only irreversibles. */ - if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized) + if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized && + inst->inst_index >= 0) EVTcreate_state(ckt, inst->inst_index); /* Loop through all connections on the instance and setup */ diff --git a/src/xspice/icm/dlmain.c b/src/xspice/icm/dlmain.c index 87f6050f9..6c93d4c4b 100644 --- a/src/xspice/icm/dlmain.c +++ b/src/xspice/icm/dlmain.c @@ -340,6 +340,11 @@ double cm_netlist_get_l(void) { return (coreitf->dllitf_cm_netlist_get_l)(); } +void cm_irreversible(unsigned int place) +{ + (coreitf->dllitf_cm_irreversible)(place); +} + const char *cm_get_node_name(const char *port, unsigned int index) { return coreitf->dllitf_cm_get_node_name(port, index); } From 566e2938f4817c889dfbb1f177fc134d4bb53414 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 18 Oct 2023 15:03:38 +0100 Subject: [PATCH 40/48] Add XSPICE code model d_cosim, a generic adaptor for digital cosimulation. --- src/include/ngspice/cosim.h | 71 +++ src/xspice/icm/digital/d_cosim/cfunc.mod | 578 ++++++++++++++++++++++ src/xspice/icm/digital/d_cosim/ifspec.ifs | 102 ++++ src/xspice/icm/digital/modpath.lst | 1 + visualc/xspice/digital.vcxproj | 4 + 5 files changed, 756 insertions(+) create mode 100644 src/include/ngspice/cosim.h create mode 100644 src/xspice/icm/digital/d_cosim/cfunc.mod create mode 100644 src/xspice/icm/digital/d_cosim/ifspec.ifs diff --git a/src/include/ngspice/cosim.h b/src/include/ngspice/cosim.h new file mode 100644 index 000000000..dd6b97094 --- /dev/null +++ b/src/include/ngspice/cosim.h @@ -0,0 +1,71 @@ +/* Header file for the shim code between d_cosim and a co-simulator. */ + +#if __cplusplus +extern "C" { +#endif + +/* A value of this type controls how the step() function is called. + * The normal method is to call step() to advance to the time of a + * queued input, then supply the input, then call step again to + * advance to the next input time or the end of the current SPICE + * timestep. But Verilator does nothing without an input change, + * so step() must be called after input. + */ + +typedef enum {Normal, After_input} Cosim_method; + +/* Structure used by Cosim_setup() to pass and return + * co-simulation interface information. + */ + +struct co_info { + /* The co-simulator must set the number of ports in Cosim_setup(). */ + + unsigned int in_count; + unsigned int out_count; + unsigned int inout_count; + + /* The co-simulator may specify a function to be called just before + * it is unloaded at the end of a simulation run. + */ + + void (*cleanup)(struct co_info *); + + /* Function called by SPICE to advance the co-simulation. + * A pointer to this structure is passed, so it has access to its handle + * and the target simulation time, vtime. The co-simulator should + * pause the step when output is produced and update vtime. + */ + + void (*step)(struct co_info *pinfo); // Advance simulation. + + /* Function called by SPICE to pass input to input and inout ports. + * (Inouts after inputs.) + * Called as: + * struct co_info info; + * (*in_fn)(&info, bit_number, &value); + * Function provided by co-simulator. + */ + + void (*in_fn)(struct co_info *, unsigned int, Digital_t *); + + /* Function called by co-simulator to report output on + * output and inout ports. (Inouts after outputs.) + * Called as: + * struct co_info *p_info; + * (*out_fn)(p_info, bit_number, &value); + * It will usually be called inside a call to step(). + */ + + void (*out_fn)(struct co_info *, unsigned int, Digital_t *); + void *handle; // Co-simulator's private handle + double vtime; // Time in the co-simulation. + Cosim_method method; // May be set in Cosim_setup; +}; + +extern void Cosim_setup(struct co_info *pinfo); // This must exist. +extern void Cosim_step(struct co_info *pinfo); // Exists for Verilator. + +#if __cplusplus +} +#endif diff --git a/src/xspice/icm/digital/d_cosim/cfunc.mod b/src/xspice/icm/digital/d_cosim/cfunc.mod new file mode 100644 index 000000000..fb653a47d --- /dev/null +++ b/src/xspice/icm/digital/d_cosim/cfunc.mod @@ -0,0 +1,578 @@ +/* Code model d_cosim. + * + * XSPICE code model for running a co-simulation with no support + * for abandoning the current timestep. + */ + +#include +#include +#include + +#if defined (__MINGW32__) || defined (__CYGWIN__) || defined (_MSC_VER) +/* MS WINDOWS. */ +#undef BOOLEAN +#include + +#define dlopen(name, type) LoadLibrary(name) +#define dlsym(handle, name) (void *)GetProcAddress(handle, name) +#define dlclose(handle) FreeLibrary(handle) + +char *dlerror(void) // Lifted from dev.c. +{ + static const char errstr_fmt[] = + "Unable to find message in dlerr(). System code = %lu"; + static char errstr[256]; + LPVOID lpMsgBuf; + + DWORD rc = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + if (rc == 0) { /* FormatMessage failed */ + (void) sprintf(errstr, errstr_fmt, (unsigned long) GetLastError()); + } else { + snprintf(errstr, sizeof errstr, lpMsgBuf); + LocalFree(lpMsgBuf); + } + return errstr; +} /* end of function dlerror */ +#else +#include +#endif + +#include "ngspice/cosim.h" + +/* The argument passed to code model functions. */ + +#define XSPICE_ARG mif_private + +#define DBG(...) +//#define DBG(...) cm_message_printf(__VA_ARGS__) + +/* Structure used to hold queued inputs. */ + +struct pend_in { + double when; // Due time. + unsigned int which; // Index of input. + Digital_t what; // The value. +}; + +/* Structure to maintain context, pointed to by STATIC VAR cosim_instance. */ + +struct instance { + struct co_info info; // Co-simulation interface - MUST BE FIRST. + int q_index; // Queue index (last active entry). + unsigned int q_length; // Size of input queue. + struct pend_in *q; // The input queue. + unsigned int in_ports; // Number of XSPICE inputs. + unsigned int out_ports; // Number of XSPICE outputs. + unsigned int inout_ports; // Number of XSPICE inout ports. + unsigned int op_pending; // Output is pending. + Digital_t *out_vals; // The new output values. + double extra; // Margin to extend timestep. + void *so_handle; // dlopen() handle to the simulation binary. +}; + +/* Called at end of simulation run to free memory. */ + +static void callback(ARGS, Mif_Callback_Reason_t reason) +{ + struct instance *ip; + + ip = (struct instance *)STATIC_VAR(cosim_instance); + if (reason == MIF_CB_DESTROY) { + if (!ip) + return; + if (ip->info.cleanup) + (*ip->info.cleanup)(&ip->info); + if (ip->so_handle) + dlclose(ip->so_handle); + if (ip->q) + free(ip->q); + if (ip->out_vals) + free(ip->out_vals); + free(ip); + STATIC_VAR(cosim_instance) = NULL; + } +} + +/* Function called when a co-simulator output changes. + * Out-of-range values for bit_num must be ignored. + */ + +void accept_output(struct co_info *pinfo, unsigned int bit_num, Digital_t *val) +{ + struct instance *ip = (struct instance *)pinfo; // First member. + Digital_t *out_vals; // XSPICE rotating memory. + + if (bit_num >= ip->out_ports + ip->inout_ports) + return; + out_vals = (Digital_t *)cm_event_get_ptr(1, 0); + DBG("Change %s %d/%d->%d/%d vtime %g", + cm_get_node_name("d_out", bit_num), + out_vals[bit_num].state, out_vals[bit_num].strength, + val->state, val->strength, + ip->info.vtime); + if (ip->op_pending == 0) { + /* Prepare pending output. */ + + memcpy(ip->out_vals, out_vals, ip->out_ports * sizeof *ip->out_vals); + ip->op_pending = 1; + } + ip->out_vals[bit_num] = *val; +} + +/* Push pending outputs, usually sent back from the future. + * It is safe to use OUTPUT() here, although it mays seem that this + * function may be called twice in a single call to cm_d_cosim(). + * There will never be any input changes when cm_d_cosim() is called + * with pending output, as all input for the shortened time-step has + * already been processed. + */ + +static void output(struct instance *ip, ARGS) +{ + double delay; + Digital_t *out_vals; // XSPICE rotating memory + int i, j; + + delay = PARAM(delay) - (TIME - ip->info.vtime); + if (delay <= 0) { + cm_message_printf("WARNING: output scheduled with impossible " + "delay (%g) at %g.", delay, TIME); + delay = 1e-12; + } + out_vals = (Digital_t *)cm_event_get_ptr(1, 0); + + /* Output to d_out. */ + + for (i = 0; i < ip->out_ports; ++i) { + if (ip->out_vals[i].state != out_vals[i].state || + ip->out_vals[i].strength != out_vals[i].strength) { + DBG("%g: OUT %s %d/%d->%d/%d vtime %g with delay %g", + TIME, cm_get_node_name("d_out", i), + out_vals[i].state, out_vals[i].strength, + ip->out_vals[i].state, ip->out_vals[i].strength, + ip->info.vtime, delay); + *(Digital_t *)OUTPUT(d_out[i]) = out_vals[i] = ip->out_vals[i]; + OUTPUT_DELAY(d_out[i]) = delay; + OUTPUT_CHANGED(d_out[i]) = TRUE; + } else { + OUTPUT_CHANGED(d_out[i]) = FALSE; + } + } + + /* Output to d_inout. */ + + for (i = 0, j = ip->out_ports; i < ip->inout_ports; ++i, ++j) { + if (ip->out_vals[j].state != out_vals[j].state || + ip->out_vals[j].strength != out_vals[j].strength) { + DBG("%g: inOUT %s %d/%d->%d/%d vtime %g with delay %g", + TIME, cm_get_node_name("d_inout", i), + out_vals[j].state, out_vals[j].strength, + ip->out_vals[j].state, ip->out_vals[j].strength, + ip->info.vtime, delay); + *(Digital_t *)OUTPUT(d_inout[i]) = out_vals[j] = ip->out_vals[j]; + OUTPUT_DELAY(d_inout[i]) = delay; + OUTPUT_CHANGED(d_inout[i]) = TRUE; + } else { + OUTPUT_CHANGED(d_inout[i]) = FALSE; + } + } + ip->op_pending = 0; +} + +/* Run the co-simulation. Return 1 if the timestep was truncated. */ + +static int advance(struct instance *ip, ARGS) +{ + /* The co-simulator should advance to the time in ip->info.vtime, + * but should pause when output is generated and update vtime. + */ + + (*ip->info.step)(&ip->info); + + if (ip->op_pending) { + + /* The co-simulator produced some output. */ + + if (TIME - ip->info.vtime <= PARAM(delay)) { +#if 1 + DBG("Direct output with SPICE %.16g CS %.16g", + TIME, ip->info.vtime); + output(ip, XSPICE_ARG); // Normal output, unlikely. +#else + cm_event_queue((TIME + ip->info.vtime + PARAM(delay)) / 2.0); +#endif + } else { + + /* Something changed that may alter the future of the + * SPICE simulation. Truncate the current timestep so that + * SPICE will see the pending output, which currently occurred + * in the past. + */ + + DBG("Truncating timestep to %.16g", ip->info.vtime + ip->extra); + cm_analog_set_temp_bkpt(ip->info.vtime + ip->extra); + + /* Any remaining input events are in an alternate future. */ + + ip->q_index = -1; + return 1; + } + } + return 0; +} + +/* Called from the main function to run the co-simulation. */ + +static void run(struct instance *ip, ARGS) +{ + struct pend_in *rp; + double sim_started; + int i; + + if (ip->q_index < 0) { + /* No queued input, advance to current TIME. */ + + DBG("Advancing vtime without input %.16g -> %.16g", + ip->info.vtime , TIME); + ip->info.vtime = TIME; + advance(ip, XSPICE_ARG); + return; + } + + /* Scan the queue. */ + + DBG("%.16g: Running Q with %d entries", TIME, ip->q_index + 1); + sim_started = ip->info.vtime; + + for (i = 0; i <= ip->q_index; ++i) { + rp = ip->q + i; + if (rp->when <= sim_started) { + /* Not expected. */ + + cm_message_printf("Warning simulated event is in the past:\n" + "XSPICE %.16g\n" + "Event %.16g\n" + "Cosim %.16g", + TIME, rp->when, ip->info.vtime); + cm_message_printf("i=%d index=%d", i, ip->q_index); + continue; + } + + /* Step the simulation forward to the input event time. */ + + ip->info.vtime = rp->when; + if (ip->info.method == Normal && advance(ip, XSPICE_ARG)) { + ip->q_index = -1; + return; + } + + /* Pass input change to simulation. */ + + (*ip->info.in_fn)(&ip->info, rp->which, &rp->what); + while (i < ip->q_index && ip->q[i + 1].when == rp->when) { + /* Another change at the same time. */ + + ++i; + rp = ip->q + i; + (*ip->info.in_fn)(&ip->info, rp->which, &rp->what); + } + + /* Simulator requested to run after input change. */ + + if (ip->info.method == After_input && advance(ip, XSPICE_ARG)) { + ip->q_index = -1; + return; + } + } + + /* All input was processed. Advance to end of the timestep. */ + + ip->q_index = -1; + if (ip->info.method == Normal && TIME > ip->info.vtime) { + ip->info.vtime = TIME; + advance(ip, XSPICE_ARG); + } +} + +/* Check whether an input value has changed. + * To reduce the number of arguments, a struture pointer is passed. + */ + +static bool check_input(struct instance *ip, Digital_t *ovp, + struct pend_in *rp) +{ + if (ovp->state != rp->what.state || + ovp->strength != rp->what.strength) { + if (++ip->q_index < ip->q_length) { + /* Record this event. */ + + ip->q[ip->q_index] = *rp; + } else { + /* Queue is full. Handle that by forcing a shorter timestep. */ + + --ip->q_index; + while (ip->q_index >= 0 && ip->q[ip->q_index].when >= rp->when) + --ip->q_index; + if (ip->q_index >= 0) { + cm_analog_set_temp_bkpt( + (rp->when + ip->q[ip->q_index].when) / 2); + } else { + /* This should never happen. */ + + cm_message_printf("Error: Event queue overflow at %e.", + rp->when); + } + } + return true; + } + return false; +} + +/* The code model's main function. */ + +void ucm_d_cosim(ARGS) +{ + struct instance *ip; + Digital_t *in_vals; // XSPICE rotating memory + int i, index; + + if (INIT) { + int ins, outs, inouts; + unsigned int alloc_size; + void *handle; + void (*ifp)(struct co_info *); + char *fn; + + /* Initialise outputs. Done early in case of failure. */ + + outs = PORT_NULL(d_out) ? 0 : PORT_SIZE(d_out); + for (i = 0; i < outs; ++i) { + OUTPUT_STATE(d_out[i]) = ZERO; + OUTPUT_STRENGTH(d_out[i]) = STRONG; + OUTPUT_DELAY(d_out[i]) = PARAM(delay); + } + + inouts = PORT_NULL(d_inout) ? 0 : PORT_SIZE(d_inout); + for (i = 0; i < inouts; ++i) { + OUTPUT_STATE(d_inout[i]) = ZERO; + OUTPUT_STRENGTH(d_inout[i]) = STRONG; + OUTPUT_DELAY(d_inout[i]) = PARAM(delay); + } + + /* Load the shared library containing the co-simulator. */ + + fn = PARAM(simulation); + handle = dlopen(fn, RTLD_LAZY | RTLD_LOCAL); + if (!handle) { + cm_message_send("Failed to load simulation binary. " + "Try setting LD_LIBRARY_PATH."); + cm_message_send(dlerror()); + return; + } + ifp = dlsym(handle, "Cosim_setup"); + if (*ifp == NULL) { + cm_message_printf("ERROR: no entry function in %s", fn); + cm_message_send(dlerror()); + dlclose(handle); + return; + } + + /* Get the instance data and initialise it. */ + + ip = (struct instance *)calloc(1, sizeof *ip); + if (!ip) + goto no_ip; + ip->so_handle = handle; + ip->info.vtime = 0.0; + ip->info.out_fn = accept_output; + CALLBACK = callback; + + /* Store the simulation interface information. */ + + (*ifp)(&ip->info); + + /* Check lengths. */ + + ins = PORT_NULL(d_in) ? 0 : PORT_SIZE(d_in); + if (ins != ip->info.in_count) { + cm_message_printf("Warning: mismatched XSPICE/co-simulator " + "input counts: %d/%d.", + ins, ip->info.in_count); + } + if (outs != ip->info.out_count) { + cm_message_printf("Warning: mismatched XSPICE/co-simulator " + "output counts: %d/%d.", + outs, ip->info.out_count); + } + + if (inouts != ip->info.inout_count) { + cm_message_printf("Warning: mismatched XSPICE/co-simulator " + "inout counts: %d/%d.", + inouts, ip->info.inout_count); + } + + /* Create input queue and output buffer. */ + + ip->q_index = -1; + ip->q_length = PARAM(queue_size); + ip->in_ports = ins; + ip->out_ports = outs; + ip->inout_ports = inouts; + if (ins + inouts > ip->q_length) { + cm_message_send("WARNING: Input queue size should be greater than " + "number of input ports. Size increased."); + ip->q_length = ins + inouts + 16; + } + alloc_size = ip->q_length * sizeof (struct pend_in); + ip->q = (struct pend_in *)malloc(alloc_size); + if (!ip->q) + goto no_q; + ip->op_pending = 0; + ip->out_vals = (Digital_t *)calloc(outs + inouts, sizeof (Digital_t)); + if (!ip->out_vals) + goto no_out_vals; + ip->extra = PARAM(delay) / 3; // FIXME? + STATIC_VAR(cosim_instance) = ip; + + /* Allocate XSPICE rotating storage to track changes. */ + + cm_event_alloc(0, (ins + inouts) * sizeof (Digital_t)); + cm_event_alloc(1, (outs + inouts) * sizeof (Digital_t)); + + /* Declare irreversible. */ + + if (PARAM(irreversible) > 0) + cm_irreversible(PARAM(irreversible)); + return; + + /* Handle malloc failures. */ + no_out_vals: + free(ip->q); + no_q: + free(ip); + no_ip: + cm_message_send("No memory!"); + return; + } + + ip = STATIC_VAR(cosim_instance); + if (!ip) { + int ports; + + /* Error state. Do nothing at all. */ + + ports = PORT_NULL(d_out) ? 0 : PORT_SIZE(d_out); + for (i = 0; i < ports; ++i) + OUTPUT_CHANGED(d_out[i]) = FALSE; + ports = PORT_NULL(d_inout) ? 0 : PORT_SIZE(d_inout); + for (i = 0; i < ports; ++i) + OUTPUT_CHANGED(d_inout[i]) = FALSE; + return; + } + in_vals = (Digital_t *)cm_event_get_ptr(0, 0); + + if (TIME == 0.0) { + /* Starting, so inputs may be settling. */ + + for (i = 0; i < ip->in_ports; ++i) { + Digital_t ival; + + ival = *(Digital_t *)INPUT(d_in[i]); + (*ip->info.in_fn)(&ip->info, i, &ival); + in_vals[i] = ival; + } + + for (i = 0; i < ip->out_ports; ++i) + OUTPUT_CHANGED(d_out[i]) = FALSE; + + for (i = 0; i < ip->inout_ports; ++i) { + Digital_t ival; + + ival = *(Digital_t *)INPUT(d_inout[i]); + (*ip->info.in_fn)(&ip->info, i + ip->in_ports, &ival); + in_vals[i + ip->in_ports] = ival; + OUTPUT_CHANGED(d_inout[i]) = FALSE; + } + return; + } + + if (CALL_TYPE == ANALOG) // Belt and braces + return; + + /* Check for pending output. */ + + if (ip->op_pending) { + output(ip, XSPICE_ARG); + } else { + for (i = 0; i < ip->out_ports; ++i) + OUTPUT_CHANGED(d_out[i]) = FALSE; + for (i = 0; i < ip->inout_ports; ++i) + OUTPUT_CHANGED(d_inout[i]) = FALSE; + } + + /* Check TIME as it may have gone backwards after a failed time-step. */ + + index = ip->q_index; + while (index >= 0 && TIME < ip->q[index].when) + --index; + ip->q_index = index; + + if (CALL_TYPE == EVENT) { + struct pend_in input; + unsigned int limit, max; + + /* New input is expected here. */ + + input.when = TIME; + + limit = ip->info.in_count + ip->info.inout_count; + max = limit < ip->in_ports ? limit : ip->in_ports; + limit -= max; + + for (input.which = 0; input.which < max; ++input.which) { + input.what = *(Digital_t *)INPUT(d_in[input.which]); + if (check_input(ip, in_vals + input.which, &input)) { + DBG("%.16g: IN %s %d/%d->%d/%d", + TIME, cm_get_node_name("d_in", input.which), + in_vals[input.which].state, in_vals[input.which].strength, + input.what.state, input.what.strength); + in_vals[input.which] = input.what; + } + } + + if (limit > ip->inout_ports) + limit = ip->inout_ports; + for (i = 0; i < limit; ++i, ++input.which) { + input.what = *(Digital_t *)INPUT(d_inout[i]); + if (check_input(ip, in_vals + input.which, &input)) { + DBG("%.16g: INout %s %d/%d->%d/%d", + TIME, cm_get_node_name("d_inout", i), + in_vals[input.which].state, in_vals[input.which].strength, + input.what.state, input.what.strength); + in_vals[ip->in_ports + i] = input.what; + } + } + } else if (CALL_TYPE == STEP_PENDING) { + /* The current timestep succeeded. Run the co-simulator code + * forward, replaying any saved input events. + */ + + if (TIME <= ip->info.vtime) + cm_message_printf("XSPICE time is behind vtime:\n" + "XSPICE %.16g\n" + "Cosim %.16g", + TIME, ip->info.vtime); + run(ip, XSPICE_ARG); + } +} diff --git a/src/xspice/icm/digital/d_cosim/ifspec.ifs b/src/xspice/icm/digital/d_cosim/ifspec.ifs new file mode 100644 index 000000000..8138b53f2 --- /dev/null +++ b/src/xspice/icm/digital/d_cosim/ifspec.ifs @@ -0,0 +1,102 @@ +/* Copyright 2023 Giles Atkinson +SUMMARY + + This file contains the interface specification file for the + d_cosim code model for general digital co-simulation. + +=============================================================================*/ + +NAME_TABLE: + +Spice_Model_Name: d_cosim +C_Function_Name: ucm_d_cosim +Description: "Bridge to an irreversible digital model" + +PORT_TABLE: + +Port_Name: d_in +Description: "digital input" +Direction: in +Default_Type: d +Allowed_Types: [d] +Vector: yes +Vector_Bounds: [0 -] +Null_Allowed: yes + +PORT_TABLE: + +Port_Name: d_out +Description: "digital output" +Direction: out +Default_Type: d +Allowed_Types: [d] +Vector: yes +Vector_Bounds: [0 -] +Null_Allowed: yes + +PORT_TABLE: + +Port_Name: d_inout +Description: "digital bidirectional port" +Direction: inout +Default_Type: d +Allowed_Types: [d] +Vector: yes +Vector_Bounds: [0 -] +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: delay +Description: "output delay time" +Data_Type: real +Default_Value: 1.0e-9 +Limits: [1e-12 -] +Vector: no +Vector_bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: simulation +Description: "A shared library containing a digital model" +Data_Type: string +Default_Value: - +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: no + +/* Instances maintain an internal input event queue that should be at least + * as large as the number of inputs. Performance with clocked logic may + * be improved by making it larger than (2 * F) / MTS, where F is + * the clock frequency and MTS is the maximum timestep for .tran. + */ + +PARAMETER_TABLE: + +Parameter_Name: queue_size +Description: "input queue size" +Data_Type: int +Default_Value: 128 +Limits: [1 -] +Vector: no +Vector_bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: irreversible +Description: "Parameter passed to library function cm_irreversible()" +Data_Type: int +Default_Value: 1 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +STATIC_VAR_TABLE: + +Static_Var_Name: cosim_instance +Data_Type: pointer +Description: "Per-instance structure" diff --git a/src/xspice/icm/digital/modpath.lst b/src/xspice/icm/digital/modpath.lst index 323c94c19..0cbd2e973 100644 --- a/src/xspice/icm/digital/modpath.lst +++ b/src/xspice/icm/digital/modpath.lst @@ -29,3 +29,4 @@ d_tff d_tristate d_xnor d_xor +d_cosim diff --git a/visualc/xspice/digital.vcxproj b/visualc/xspice/digital.vcxproj index 47e7cb7d7..bd4d42487 100644 --- a/visualc/xspice/digital.vcxproj +++ b/visualc/xspice/digital.vcxproj @@ -322,6 +322,10 @@ ..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories) + + + ..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories) + From 3ba314d4bc0a7e07a450d89b1b4d9275f59ec40e Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Sun, 1 Oct 2023 17:14:48 +0100 Subject: [PATCH 41/48] New interpreter commands strstr, strslice, fopen, fread and fclose. --- src/frontend/Makefile.am | 2 + src/frontend/com_fileio.c | 181 ++++++++++++++++++++++++++++++++++++++ src/frontend/com_fileio.h | 9 ++ src/frontend/com_strcmp.c | 51 +++++++++++ src/frontend/com_strcmp.h | 5 +- src/frontend/commands.c | 49 +++++++++++ visualc/vngspice.vcxproj | 4 +- 7 files changed, 298 insertions(+), 3 deletions(-) create mode 100644 src/frontend/com_fileio.c create mode 100644 src/frontend/com_fileio.h diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am index 14ae122ef..8781b8610 100644 --- a/src/frontend/Makefile.am +++ b/src/frontend/Makefile.am @@ -34,6 +34,8 @@ libfte_la_SOURCES = \ com_dump.h \ com_echo.c \ com_echo.h \ + com_fileio.c \ + com_fileio.h \ com_ghelp.c \ com_ghelp.h \ com_gnuplot.h \ diff --git a/src/frontend/com_fileio.c b/src/frontend/com_fileio.c new file mode 100644 index 000000000..078baa2c3 --- /dev/null +++ b/src/frontend/com_fileio.c @@ -0,0 +1,181 @@ +/* Commands for opening and reading arbitrary files. */ + +#include +#include +#include + +#include "ngspice/ngspice.h" + +#include "ngspice/bool.h" +#include "ngspice/wordlist.h" + +#include "com_strcmp.h" +#include "variable.h" + +/* Track open files with these structures, indexed by the underlying + * descriptor. Not many should be needed. + */ + +#define MAX_OPEN_FILES 20 +#define MAX_TEXT_LINE 8192 + +static struct { + FILE *fp; + char *name; +} Open_Files[MAX_OPEN_FILES]; + +/* Check whether error messages should be suppressed. That is useful when + * opening a file to see if it exists. + */ + +static int verbose(void) +{ + return !cp_getvar("silent_fileio", CP_BOOL, NULL, 0); +} + +/* fopen handle file_name [mode] + * + * For example: fopen handle result.txt r + * + * The underlying file descriptor (or -1) is returned in the variable "handle". + */ + +void com_fopen(wordlist *wl) +{ + char *var, *file_name, *mode; + FILE *fp; + int fd; + + var = wl->wl_word; + wl = wl->wl_next; + file_name = cp_unquote(wl->wl_word); + wl = wl->wl_next; + mode = wl ? cp_unquote(wl->wl_word) : "r"; + fp = fopen(file_name, mode); + if (fp) { + fd = fileno(fp); + if (fd < MAX_OPEN_FILES) { + if (Open_Files[fd].fp) // Not expected! + fclose(Open_Files[fd].fp); + if (Open_Files[fd].name) + tfree(Open_Files[fd].name); + Open_Files[fd].fp = fp; + Open_Files[fd].name = copy(file_name); + } else { + fclose(fp); + fprintf(stderr, + "com_fopen() cannot open %s: too many open files\n", + file_name); + fd = -1; + } + } else { + fd = -1; + if (verbose()) { + fprintf(stderr, "com_fopen() cannot open %s: %s\n", + file_name, strerror(errno)); + } + } + tfree(file_name); + if (wl) + tfree(mode); + cp_vset(var, CP_NUM, &fd); +} + +/* Command looks like: + * fread result handle [length] + * where handle is a small positive integer, result names a variable + * and length is the name of a variable used to return the length of the line. + * The returned length is -1 at EOF, -2 on failure. + */ + +void com_fread(wordlist *wl) +{ + char *handle, *result, *lvar; + int fd, length; + char buf[MAX_TEXT_LINE]; + + result = cp_unquote(wl->wl_word); + wl = wl->wl_next; + handle = cp_unquote(wl->wl_word); + fd = atoi(handle); + tfree(handle); + wl = wl->wl_next; + if (wl) + lvar = cp_unquote(wl->wl_word); + else + lvar = NULL; + + if (fd >= 0 && fd < MAX_OPEN_FILES) { + if (!Open_Files[fd].fp) { + /* Allow stdin, for example. */ + + Open_Files[fd].fp = fdopen(fd, "r"); + if (!Open_Files[fd].fp && verbose()) { + fprintf(stderr, "com_fread() cannot open handle %d\n", fd); + goto err; + } + } + + if (fgets(buf, sizeof buf, Open_Files[fd].fp)) { + length = strlen(buf); + if (length > 0 && buf[length - 1] == '\n') { + --length; + if (length > 0 && buf[length - 1] == '\r') { + /* Windows CRLF line termination. */ + + --length; + } + buf[length] = '\0'; + } else if (verbose()) { + fprintf(stderr, + "com_fread() found line in %s " + "too long for buffer\n", + Open_Files[fd].name); + } + } else { + if (feof(Open_Files[fd].fp)) { + length = -1; + } else if (verbose()) { + fprintf(stderr, + "com_fread() error reading %s: %s\n", + Open_Files[fd].name, strerror(errno)); + length = -2; + } + *buf = '\0'; + } + } else if (verbose()) { + fprintf(stderr, + "com_fread(): file handle %d is not in accepted range.\n", + fd); + err: + length = -1; + *buf = '\0'; + } + cp_vset(result, CP_STRING, buf); + tfree(result); + if (lvar) { + cp_vset(lvar, CP_NUM, &length); + tfree(lvar); + } +} + +void com_fclose(wordlist *wl) +{ + char *handle; + int fd; + + handle = cp_unquote(wl->wl_word); + fd = atoi(handle); + tfree(handle); + if (fd <= 2 || fd >= MAX_OPEN_FILES) + return; + if (Open_Files[fd].fp) { + fclose(Open_Files[fd].fp); + Open_Files[fd].fp = NULL; + } + if (Open_Files[fd].name) + tfree(Open_Files[fd].name); +} + + + diff --git a/src/frontend/com_fileio.h b/src/frontend/com_fileio.h new file mode 100644 index 000000000..ddb160555 --- /dev/null +++ b/src/frontend/com_fileio.h @@ -0,0 +1,9 @@ +#ifndef ngspice_COM_FILEIO_H +#define ngspice_COM_FILEIO_H + + +extern void com_fopen(wordlist *wl); +extern void com_fread(wordlist *wl); +extern void com_fclose(wordlist *wl); + +#endif diff --git a/src/frontend/com_strcmp.c b/src/frontend/com_strcmp.c index 014a8025a..4663d59b3 100644 --- a/src/frontend/com_strcmp.c +++ b/src/frontend/com_strcmp.c @@ -23,3 +23,54 @@ com_strcmp(wordlist *wl) tfree(s2); cp_vset(var, CP_NUM, &i); } + +/* These must be more evil still. */ + +void com_strstr(wordlist *wl) +{ + char *var, *s1, *s2; + int i; + + s1 = cp_unquote(wl->wl_next->wl_word); + s2 = cp_unquote(wl->wl_next->wl_next->wl_word); + if (*s2) { + var = strstr(s1, s2); // Search for s2 in s1 + if (var) + i = var - s1; // Offset to match + else + i = -1; + } else { + i = strlen(s1); // Length + } + tfree(s1); + tfree(s2); + cp_vset(wl->wl_word, CP_NUM, &i); +} + +void com_strslice(wordlist *wl) +{ + char *var, *s1, *tp, tmp; + int offset, length, actual; + + var = wl->wl_word; + wl = wl->wl_next; + s1 = cp_unquote(wl->wl_word); + wl = wl->wl_next; + offset = atoi(wl->wl_word); + length = atoi(wl->wl_next->wl_word); + actual = strlen(s1); + if (offset < 0) + offset = actual + offset; + if (length + offset > actual) + length = actual - offset; + if (length > 0 && offset >= 0) { + tp = s1 + offset + length; + tmp = *tp; + *tp = '\0'; + cp_vset(var, CP_STRING, s1 + offset); + *tp = tmp; + } else { + cp_vset(var, CP_STRING, ""); + } + tfree(s1); +} diff --git a/src/frontend/com_strcmp.h b/src/frontend/com_strcmp.h index cdaeaffda..e9a3dd68d 100644 --- a/src/frontend/com_strcmp.h +++ b/src/frontend/com_strcmp.h @@ -1,7 +1,8 @@ #ifndef ngspice_COM_STRCMP_H #define ngspice_COM_STRCMP_H - -void com_strcmp(wordlist *wl); +extern void com_strcmp(wordlist *wl); +extern void com_strstr(wordlist *wl); +extern void com_strslice(wordlist *wl); #endif diff --git a/src/frontend/commands.c b/src/frontend/commands.c index 8e2988c56..36f30f72f 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -82,6 +82,7 @@ #include "resource.h" #include "diff.h" #include "com_strcmp.h" +#include "com_fileio.h" #include "ngspice/randnumb.h" #include "../spicelib/analysis/com_optran.h" #include "com_wr_ic.h" @@ -613,6 +614,30 @@ struct comm spcp_coms[] = { { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, NULL, "varname s1 s2 : Set $varname to strcmp(s1, s2)." } , + { "strstr", com_strstr, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, + NULL, + "varname s1 s2 : Set $varname to strstr(s1, s2)." } , + { "strslice", com_strslice, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 4, 4, + NULL, + "varname s1 offset length : " + "Set $varname to s1[offset ... offset+length]" } , + { "fopen", com_fopen, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle file_name [mode] : " + "Open file_name with mode, return handle in $handle" } , + { "fread", com_fread, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle result [length] : " + "Read a line from open file handle, " + "data in $result, status in $length" } , + { "fclose", com_fclose, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 1, 1, + NULL, + "handle : Close open file" } , { "linearize", com_linearize, FALSE, FALSE, { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, @@ -1033,6 +1058,30 @@ struct comm nutcp_coms[] = { { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, NULL, "varname s1 s2 : Set $varname to strcmp(s1, s2)." } , + { "strstr", com_strstr, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, + NULL, + "varname s1 s2 : Set $varname to strstr(s1, s2)." } , + { "strslice", com_strslice, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 4, 4, + NULL, + "varname s1 offset length : " + "Set $varname to s1[offset ... offset+length]" } , + { "fopen", com_fopen, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle file_name [mode] : " + "Open file_name with mode, return handle in $handle" } , + { "fread", com_fread, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle result [length] : " + "Read a line from open file handle, " + "data in $result, status in $length" } , + { "fclose", com_fclose, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 1, 1, + NULL, + "handle : Close open file" } , { "linearize", com_linearize, TRUE, FALSE, { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, diff --git a/visualc/vngspice.vcxproj b/visualc/vngspice.vcxproj index 279cc36d8..476250aa7 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -885,6 +885,7 @@ + @@ -1498,6 +1499,7 @@ + @@ -2894,4 +2896,4 @@ - \ No newline at end of file + From 89851872eda8dfa738b04305e662daa10d20da3c Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 18 Oct 2023 13:21:24 +0100 Subject: [PATCH 42/48] When substituting interpreter variables, allow '$' to be part of the variable expresstion only within '[]' or '()'. That lets forms like "$a$b" to be treated as two substitutions, not one. --- src/frontend/variable.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/frontend/variable.c b/src/frontend/variable.c index 3a241bb2a..5bc2c1f93 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -789,7 +789,7 @@ cp_getvar(char *name, enum cp_types type, void *retval, size_t rsize) * isset is TRUE if the variable is being set, FALSE if unset. Also * required is a routine cp_enqvar(name) which returns a struct * variable *, which allows the host program to provide values for - * non-cshpar variables. See line 855 */ + * non-cshpar variables. */ /* Non-alphanumeric characters that may appear in variable names. < is very * special... @@ -806,10 +806,11 @@ cp_getvar(char *name, enum cp_types type, void *retval, size_t rsize) * Return value * Address of the first character after the variable name. */ -char *span_var_expr(char *t) +char *span_var_expr(char *s) { - int parenthesis = 0; - int brackets = 0; + char *t = s; + int parenthesis = 0; + int brackets = 0; while (*t && (isalnum_c(*t) || strchr(VALIDCHARS, *t))) switch (*t++) @@ -832,6 +833,14 @@ char *span_var_expr(char *t) if (--parenthesis <= 0) return t; break; + case '$': + if (brackets <= 0 && parenthesis <= 0) { + if (t == s + 1) // Special case: "$$". + return t; + else + return t-1; + } + break; default: break; } @@ -1000,6 +1009,8 @@ wordlist *vareval(/* NOT const */ char *string) if (eq(v->va_name, string)) break; if (!v && isdigit_c(*string)) { + /* Treat $i for some integer i as argv[i] - positional parameters. */ + for (v = variables; v; v = v->va_next) { if (eq(v->va_name, "argv")) { break; From 07ce9c788e8f401e29f2535352f383821a253b56 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 18 Oct 2023 13:37:57 +0100 Subject: [PATCH 43/48] Recognise *ng_script_with_params" in the first line of the first program input file to mean that the file is a pure interpreter script that receives any remaining command arguments. In winmain.c ensure that the argv array is NULL-terminated and tidy some code. --- src/frontend/inp.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-- src/main.c | 34 ++++++++++++++++++--- src/winmain.c | 14 ++++----- 3 files changed, 107 insertions(+), 16 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index 73208f5c5..3a1a22300 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -32,6 +32,7 @@ Author: 1985 Wayne A. Christopher #include "subckt.h" #include "spiceif.h" #include "com_let.h" +#include "com_set.h" #include "com_commands.h" #ifdef XSPICE @@ -637,10 +638,71 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) * the cards .control and .endc, unless comfile is TRUE, in which * case every line must be a front-end command. There are too * many problems with matching the first word on the line. */ + ld = deck; if (comfile) { + bool with_params = FALSE; + +#ifndef SHARED_MODULE + if (ciprefix("*ng_script_with_params", deck->line)) { + extern char **Copy_of_argv; // main.c + extern int optind; // Library function getopt() + static const char header[] = "argc = %u argv = ( "; + wordlist *setarg; + unsigned int argc, size; + char *p_buf_active; /* buffer in use */ + char buf[BSIZE_SP]; + + /* Not just a command script, but one requesting arguments + * from the program's command line. + * This is similar to cp_oddcomm() (cpitf.c), + * but arguments are taken from the original program command. + */ + + with_params = TRUE; + size = sizeof header + 10; // Allow for %u and close. + for (argc = 0; Copy_of_argv[optind + argc]; ++argc) + size += strlen(Copy_of_argv[optind + argc]); + size += 3 * argc; // Spaces and quotes. + if (size <= sizeof buf) + p_buf_active = buf; + else + p_buf_active = TMALLOC(char, size); + + /* Fill the buffer. */ + + size = sprintf(p_buf_active, header, argc); + while (Copy_of_argv[optind]) { + char c, *fmt; + + c = Copy_of_argv[optind][0]; + if ((c >= '0' && c <= '9') || c == '+' || c == '-' || !c) { + /* Looks like a number or empty string - quote it. */ + + fmt = " \"%s\""; + } else { + fmt = " %s"; + } + size += sprintf(p_buf_active + size, fmt, + Copy_of_argv[optind++]); + } + strcpy(p_buf_active + size, " )"); + + /* Treat the buffer as a "set" command to create argv and argc. */ + + setarg = cp_lexer(p_buf_active); + com_set(setarg); + wl_free(setarg); + + /* Free buffer allocation if made */ + + if (p_buf_active != buf) + txfree(p_buf_active); + } +#endif /* Process each command, except 'option' which is assembled in a list and ingnored here */ + for (dd = deck; dd; dd = ld) { ld = dd->nextcard; if ((dd->line[0] == '*') && (dd->line[1] != '#')) @@ -655,6 +717,12 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) cp_evloop(dd->line); } } + + if (with_params) { + cp_remvar("argc"); + cp_remvar("argv"); + } + /* free the control deck */ line_free(deck, TRUE); /* set to NULL to allow generation of a new dbs */ @@ -943,10 +1011,11 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) } /* merge the two option line structs - com_options (comfile == TRUE, filled in from spinit, .spiceinit, and *ng_sript), and - options (comfile == FALSE, filled in from circuit with .OPTIONS) + com_options (comfile == TRUE, filled in from spinit, + .spiceinit, and *ng_script), and options + (comfile == FALSE, filled in from circuit with .OPTIONS) into options, thus keeping com_options, - options is loaded into circuit and freed when circuit is removed */ + options is loaded into circuit and freed when circuit is removed */ options = line_reverse(line_nconc(options, inp_deckcopy(com_options))); /* List of all expressions found in instance and .model lines */ diff --git a/src/main.c b/src/main.c index e3999a865..174931fd5 100644 --- a/src/main.c +++ b/src/main.c @@ -164,6 +164,8 @@ double EpsNorm, VNorm, NNorm, LNorm, TNorm, JNorm, GNorm, ENorm; /* end cider globals */ #endif /* CIDER */ +char **Copy_of_argv; // Used to recover args for *ng_script_with_params files + struct variable *(*if_getparam)(CKTcircuit *ckt, char **name, char *param, int ind, int do_model); /* static functions */ @@ -1382,13 +1384,16 @@ int main(int argc, char **argv) while (optind < argc) { char *arg = argv[optind++]; FILE *tp; + /* Copy the the path of the first filename only */ if (!Infile_Path) { Infile_Path = ngdirname(arg); } - /* unquote the input string, needed if it results from double clicking the filename */ #if defined(HAS_WINGUI) + /* Unquote the input string, needed if it results + * from double clicking the filename. + */ arg = cp_unquote(arg); #endif /* Copy all the arguments into the temporary file */ @@ -1408,8 +1413,9 @@ int main(int argc, char **argv) } } - /* Copy the input file name which otherwise will be lost due to the - temporary file */ + /* Copy the input file name which otherwise will be lost + * due to the temporary file. + */ dname = copy(arg); #if defined(HAS_WINGUI) /* write source file name into source window */ @@ -1418,11 +1424,32 @@ int main(int argc, char **argv) tfree(arg); #endif + if (!gotone) { + char line[256]; + + /* Check for "*ng_script_with_params" as first line. */ + + if (fgets(line, sizeof line, tp) && + ciprefix("*ng_script_with_params", line)) { + /* Special script file: remaining arguments are + * script parameters. + */ + + fclose(tempfile); + tempfile = tp; + Copy_of_argv = argv; + break; + } else { + fseek(tp, 0L, SEEK_SET); + gotone = TRUE; + } + } append_to_stream(tempfile, tp); fclose(tp); } fseek(tempfile, 0L, SEEK_SET); + gotone = FALSE; // Re-use if (tempfile && (!err || !ft_batchmode)) { /* Copy the input file name for becoming another file search path */ @@ -1450,7 +1477,6 @@ int main(int argc, char **argv) } if (ft_batchmode) { - int error3 = 1; /* If we get back here in batch mode then something is wrong, diff --git a/src/winmain.c b/src/winmain.c index 2fb22aa0c..1bf3bb5fb 100644 --- a/src/winmain.c +++ b/src/winmain.c @@ -850,7 +850,6 @@ ElementWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) static int MakeArgcArgv(char *cmdline, int *argc, char ***argv) { - char *pC1; /* a temporary character pointer */ char *pWorkString = NULL; /* a working copy of cmdline */ int i; /* a loop counter */ int quoteflag = 0; /* for the finite state machine parsing cmdline */ @@ -899,13 +898,13 @@ MakeArgcArgv(char *cmdline, int *argc, char ***argv) #endif /* If we still have a string left, parse it for all the arguments. */ - if (strlen(pWorkString)) + if (pWorkString[0]) { /* This could probably be done with strtok as well but strtok is destructive if I wanted to look for " \"" and I couldn't tell what delimiter that I had bumped against */ - for (i = 0; i < (signed)strlen(pWorkString); i++) + for (i = 0; pWorkString[i]; i++) switch (pWorkString[i]) { case SPACE: @@ -935,7 +934,7 @@ MakeArgcArgv(char *cmdline, int *argc, char ***argv) } } /* malloc an argv */ - tmpargv = (char**) malloc((unsigned)numargs * sizeof(char *)); + tmpargv = (char**) malloc((unsigned)(numargs + 1) * sizeof(char *)); if (NULL == tmpargv) { status = -1; @@ -949,18 +948,15 @@ MakeArgcArgv(char *cmdline, int *argc, char ***argv) deli[0] = DELIMITER; deli[1] = '\0'; /* delimiter for strtok */ - pC1 = NULL; /* Now actually strdup all the arguments out of the string and store them in the argv */ for (i = 1; i < numargs; i++) { - if (NULL == pC1) - pC1 = pWorkString; - if (i == 1) - tmpargv[i] = copy(strtok(pC1, deli)); + tmpargv[i] = copy(strtok(pWorkString, deli)); else tmpargv[i] = copy(strtok(NULL, deli)); } + tmpargv[i] = NULL; /* copy the working values over to the arguments */ *argc = numargs; From 597268cc26e12cc67a4826ca28055c78d370e3de Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Tue, 21 Nov 2023 17:12:23 +0000 Subject: [PATCH 44/48] Treat input files names passed a start-up the same as those entered as commands or called as scripts: search for them using $sourcepath. --- src/main.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index 174931fd5..0dc5db052 100644 --- a/src/main.c +++ b/src/main.c @@ -1406,10 +1406,16 @@ int main(int argc, char **argv) tp = fopen(p, "r"); tfree(p); } + if (!tp) { - perror(arg); - err = 1; - break; + /* Try and find it in a directory in $sourcepath. */ + + tp = inp_pathopen(arg, "r"); + if (!tp) { + perror(arg); + err = 1; + break; + } } } From a92c569b72598dc92317bf71026c42d978bc21aa Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 22 Nov 2023 16:16:46 +0000 Subject: [PATCH 45/48] Prevent some strings being mis-recognised by strstr(), including one that causes a false error message from the vlnggen script. --- src/frontend/inpcom.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 710b2c1f0..327311b71 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1591,8 +1591,11 @@ struct inp_read_t inp_read( FILE *fp, int call_depth, const char *dir_name, /* add Inp_Path to buffer while keeping the sourcepath variable contents */ if (ciprefix("set", buffer)) { - char *p = strstr(buffer, "sourcepath"); - if (p) { + char *p; + + p = skip_ws(buffer + 3); // Next word + if (strncmp(p, "sourcepath", 10) == 0 && + skip_non_ws(p) == p + 10) { p = strchr(buffer, ')'); if (p) { *p = 0; // clear ) and insert Inp_Path in between @@ -3110,7 +3113,8 @@ void inp_casefix(char *string) /* Special treatment of code model file input. */ if (ciprefix(".model", string)) - tmpstr = strstr(string, "file="); + tmpstr = strstr(string, "file=\""); + #endif keepquotes = ciprefix(".param", string); // Allow string params @@ -3328,7 +3332,8 @@ static char *inp_fix_subckt(struct names *subckt_w_params, char *s) char *equal, *beg, *buffer, *ptr1, *ptr2, *new_str; equal = strchr(s, '='); - if (equal && !strstr(s, "params:")) { + if (equal && + (!strstr(s, "params:") || !isspace_c(s[-1]))) { /* get subckt name (ptr1 will point to name) */ ptr1 = skip_non_ws(s); ptr1 = skip_ws(ptr1); @@ -3499,7 +3504,7 @@ static void inp_fix_for_numparam( if (ciprefix(".subckt", c->line) || ciprefix("x", c->line)) { /* remove params: */ char *str_ptr = strstr(c->line, "params:"); - if (str_ptr) + if (str_ptr && isspace_c(str_ptr[-1])) memcpy(str_ptr, " ", 7); } @@ -3800,13 +3805,14 @@ static int inp_fix_subckt_multiplier(struct names *subckt_w_params, char *subckt_param_names[], char *subckt_param_values[]) { struct card *card; - char *new_str; + char *new_str, *s; subckt_param_names[num_subckt_params] = copy("m"); subckt_param_values[num_subckt_params] = copy("1"); num_subckt_params++; - if (!strstr(subckt_card->line, "params:")) { + s = strstr(subckt_card->line, "params:"); + if (!s || !isspace_c(s[-1])) { new_str = tprintf("%s params: m=1", subckt_card->line); add_name(subckt_w_params, get_subckt_model_name(subckt_card->line)); } From c18447f9f5f1e5d5f2eea188e434119a64c70d2b Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Mon, 27 Nov 2023 18:02:10 +0000 Subject: [PATCH 46/48] Add the support files for co-simulation with Verilog code compiled by Verilator. Also add script files to Visual Studio builds that are already installed by the Makefile builds. --- examples/xspice/verilator/README.txt | 10 + examples/xspice/verilator/adc.cir | 103 +++++++++ examples/xspice/verilator/adc.v | 45 ++++ src/xspice/Makefile.am | 12 +- src/xspice/verilog/MSVC.CMD | 1 + src/xspice/verilog/README.txt | 3 + src/xspice/verilog/verilator_main.cpp | 13 ++ src/xspice/verilog/verilator_shim.cpp | 157 +++++++++++++ src/xspice/verilog/vlnggen | 316 ++++++++++++++++++++++++++ visualc/make-install-vngspice.bat | 21 +- visualc/make-install-vngspiced.bat | 21 +- 11 files changed, 689 insertions(+), 13 deletions(-) create mode 100644 examples/xspice/verilator/README.txt create mode 100644 examples/xspice/verilator/adc.cir create mode 100644 examples/xspice/verilator/adc.v create mode 100644 src/xspice/verilog/MSVC.CMD create mode 100644 src/xspice/verilog/README.txt create mode 100644 src/xspice/verilog/verilator_main.cpp create mode 100644 src/xspice/verilog/verilator_shim.cpp create mode 100644 src/xspice/verilog/vlnggen diff --git a/examples/xspice/verilator/README.txt b/examples/xspice/verilator/README.txt new file mode 100644 index 000000000..e919ceab5 --- /dev/null +++ b/examples/xspice/verilator/README.txt @@ -0,0 +1,10 @@ +The circuit adc.cir in this directory illustrates the use of the d_cosim +XSPICE code model as a container for a Verilog simulation. Before the +simulation can be run, the Verilog code must be compiled by Verilator +using the command: + + ngspice vlnggen adc.v + +That should create a shared library file, adc.so (or adc.DLL on Windows) +that will be loaded by the d_cosim code model. The compiled Verilog code that +it contains will be executed during simulation. diff --git a/examples/xspice/verilator/adc.cir b/examples/xspice/verilator/adc.cir new file mode 100644 index 000000000..133a7dbf6 --- /dev/null +++ b/examples/xspice/verilator/adc.cir @@ -0,0 +1,103 @@ +Simulation of a switched-capacitor SAR ADC with Verilator and d_cosim + +.subckt sar_adc input vref start valid d5 d4 d3 d2 d1 d0 clk + +* A transmission gate connects the input to the capacitor set. + +xsample input iin sample vref tgate +rin iin test_v 1k + +* Capacitors and controlling inverters + +xb5 test_v vref d5 ccap c=1p +xb4 test_v vref d4 ccap c={1p / 2} +xb3 test_v vref d3 ccap c={1p / 4} +xb2 test_v vref d2 ccap c={1p / 8} +xb1 test_v vref d1 ccap c={1p / 16} +xb0 test_v vref d0 ccap c={1p / 32} +clast test_v 0 {1p / 32} + +* An XSPICE ADC bridge functions as a comparator. + +acomp [%vd(test_v vref)] [comp] comparator +.model comparator adc_bridge in_low=0 in_high=0 + +* The digital portion of the circuit is specified in compiled Verilog. +* Outputs inverted to cancel the inverter in subcircuit ccap, +* and produce the correct numerical output value. + +adut [ Clk Comp Start] [Sample Valid ~d5 ~d4 ~d3 ~d2 ~d1 ~d0] null dut +.model dut d_cosim simulation="./adc.so" +.ends // SUBCKT sar_adc + +* Some MOS transistors complete the circuit. +* Models from https://homepages.rpi.edu/~sawyes/AIMSPICE_TutorialManual.pdf + +.model p1 pmos ++ level=2 vto=-0.5 kp=8.5e-6 gamma=0.4 phi=0.65 lambda=0.05 xj=0.5e-6 +.model n1 nmos ++ level=2 vto=0.5 kp=24e-6 gamma=0.15 phi=0.65 lambda=0.015 xj=0.5e-6 + +* Use those for an inverter. + +.subckt ainv in out vdd +mn out in 0 0 n1 +mp out in vdd vdd p1 +.ends + +* A transmission gate modelled by a switch. + +.subckt mos_tgate a b ctl vdd +mn a ctl b b n1 +xinv ctl ictl vdd ainv +mp b ictl a a p1 +.ends + +.subckt tgate a b ctl vdd +switch a b ctl 0 tg +.model tg sw vt=1.5 ron=2k +.ends + +* The per-bit subcircuit in the adc + +.subckt ccap in vcc ctl c=10p +xinv ctl tail vcc ainv +cb in tail {c} +.ends + +**** End of the ADC and its subcircuits. Begin test circuit **** + +.param vcc=3.3 +vcc vcc 0 {vcc} + +* Digital clock signal + +aclock 0 clk clock +.model clock d_osc cntl_array=[-1 1] freq_array=[1Meg 1Meg] + +* A simple DAC so that the result may be compared to the input. + +r5 d5 sum 2 +r4 d4 sum 4 +r3 d3 sum 8 +r2 d2 sum 16 +r1 d1 sum 32 +r0 d0 sum 64 + +vamm sum 0 0 + +* Pulse the Start signal high for 1.3uS each 10uS + +Vpulse Start 0 PULSE 0 {vcc} 0.2u 10n 10n 1.3u 10u +Vtest input 0 PULSE 0 3 0 200u 200u 1u 401u + +* The ADC for testing + +xtest input vcc start valid d5 d4 d3 d2 d1 d0 clk sar_adc + + +.control +tran 100n 250u +plot input xtest.test_v vamm#branch clk/2 start/3 xtest.sample/3 valid +.endc +.end diff --git a/examples/xspice/verilator/adc.v b/examples/xspice/verilator/adc.v new file mode 100644 index 000000000..14bfcaccf --- /dev/null +++ b/examples/xspice/verilator/adc.v @@ -0,0 +1,45 @@ +// Digital control for a successive approximation ADC with switched capacitors. + +module adc(Clk, Comp, Start, Sample, Done, Result); + input wire Clk, Comp, Start; + output reg Sample, Done; + output reg [Bits - 1 : 0] Result; + + parameter Bits=6; + + reg [Bits - 1 : 0] SR; + reg Running; + + initial begin + $display("ADC simulation starting"); + Done = 0; + Sample = 0; + Result = 0; + Running = 0; + end + + always @(posedge(Clk)) begin + if (Running) begin + if (Sample) begin + Sample <= 0; + SR[Bits - 1] = 1; + Result[Bits - 1] = 1; + end else if (SR != 0) begin + if (Comp) + Result &= ~SR; + SR >>= 1; + Result |= SR; + if (SR == 0) begin + Running <= 0; + Done <= 1; + end + end + end else if (Start) begin + Running <= 1; + Sample <= 1; + Done <= 0; + SR = 0; + Result = 0; + end + end +endmodule diff --git a/src/xspice/Makefile.am b/src/xspice/Makefile.am index 2718b4f79..9ff281f97 100644 --- a/src/xspice/Makefile.am +++ b/src/xspice/Makefile.am @@ -1,6 +1,8 @@ # Process this file with automake -EXTRA_DIST = README examples icm xspice.c .gitignore +EXTRA_DIST = README examples icm xspice.c .gitignore \ + verilog/vlnggen \ + verilog/verilator_shim.cpp verilog/verilator_main.cpp ## This is removed because icm relies upon the existance of all other ## libs. It is currently compiled manually, last. @@ -8,7 +10,15 @@ EXTRA_DIST = README examples icm xspice.c .gitignore SUBDIRS = mif cm enh evt ipc idn cmpp icm +initdatadir = $(pkgdatadir)/scripts +initdata_DATA = verilog/vlnggen +initdata1dir = $(pkgdatadir)/scripts/src +initdata1_DATA = verilog/verilator_shim.cpp verilog/verilator_main.cpp + +initdata2dir = $(pkgdatadir)/scripts/src/ngspice +initdata2_DATA = ../include/ngspice/cosim.h ../include/ngspice/miftypes.h \ + ../include/ngspice/cmtypes.h dist-hook: rm -f "$(distdir)/icm/makedefs" rm -f "$(distdir)/icm/GNUmakefile" diff --git a/src/xspice/verilog/MSVC.CMD b/src/xspice/verilog/MSVC.CMD new file mode 100644 index 000000000..bd9ab9696 --- /dev/null +++ b/src/xspice/verilog/MSVC.CMD @@ -0,0 +1 @@ +CL /O2 /LD /EHsc /Fe..\adc.DLL /I. /IC:\mingw64\share\verilator\include\vltstd /IC:\mingw64\share\verilator\include Vlng__ALL.cpp verilator_shim.cpp C:\mingw64\share\verilator\include\verilated.cpp C:\mingw64\share\verilator\include\verilated_threads.cpp /link /DLL /EXPORT:Cosim_setup diff --git a/src/xspice/verilog/README.txt b/src/xspice/verilog/README.txt new file mode 100644 index 000000000..6eea6d832 --- /dev/null +++ b/src/xspice/verilog/README.txt @@ -0,0 +1,3 @@ +This directory contains Ngspice scripts and other files used to prepare +Verilog (and possibly VHDL) code to be included in an Ngspice simulation. +An example circuit can be found in examples/xspice/verilator. diff --git a/src/xspice/verilog/verilator_main.cpp b/src/xspice/verilog/verilator_main.cpp new file mode 100644 index 000000000..c6b6eef15 --- /dev/null +++ b/src/xspice/verilog/verilator_main.cpp @@ -0,0 +1,13 @@ +/* Dummy main() for Verilator. */ + +#include "ngspice/cmtypes.h" // For Digital_t +#include "ngspice/cosim.h" // For struct co_info and prototypes + +int main(int argc, char** argv, char**) { + struct co_info info; + + Cosim_setup(&info); + for (;;) + (*info.step)(&info); + return 0; +} diff --git a/src/xspice/verilog/verilator_shim.cpp b/src/xspice/verilog/verilator_shim.cpp new file mode 100644 index 000000000..186d4a61c --- /dev/null +++ b/src/xspice/verilog/verilator_shim.cpp @@ -0,0 +1,157 @@ +/* This is a very mangled version of Vadc__main.cpp as generated by Verilator. */ + +// Verilated -*- C++ -*- +// DESCRIPTION: main() calling loop, created with Verilator --main + +#include "verilated.h" +#include "Vlng.h" + +#include "ngspice/cmtypes.h" // For Digital_t +#include "ngspice/cosim.h" // For struct co_info and prototypes + +//====================== + +/* Structure for the input table. */ + +struct input { + const char *name; + unsigned int offset; + unsigned int count; +}; + +/* This VL_DATA macro is used in header files inputs.h, outputs.h and inouts.h + * to write functions accept_input() and step(). + * The macro is used several times with different definitions. + */ + +/* Generate the previous values table used by step(). */ + +#define VL_DATA(size, name, msb, lsb) + msb - lsb + 1 +static const unsigned int outs = 0 +#include "outputs.h" + ; +static const unsigned int inouts = 0 +#include "inouts.h" + ; + +static unsigned char previous_output[outs + inouts]; + +#undef VL_DATA + +/* The input function: it should ignore out-of-range values of index. */ + +#define VL_DATA(size, name, msb, lsb) \ + if (index >= msb - lsb + 1) { \ + index -= msb - lsb + 1; \ + } else if (msb == 0 && lsb == 0) { \ + topp->name = val ? 1 : 0; \ + return; \ + } else { \ + if (val) \ + topp->name |= (1 << (msb - index)); \ + else \ + topp->name &= (1 << (msb - index)); \ + return; \ + } + +static void accept_input(struct co_info *pinfo, + unsigned int index, Digital_t *vp) +{ + Vlng *topp = (Vlng *)pinfo->handle; + unsigned int val, offset; + + val = vp->state; + if (val == UNKNOWN) + return; // Verilator simulations are two-state. + +#include "inputs.h" + + /* For inout ports the new value must be stored to detect changes. */ + + offset = outs; +#undef VL_DATA +#define VL_DATA(size, name, msb, lsb) \ + if (index >= msb - lsb + 1) { \ + index -= msb - lsb + 1; \ + offset += msb - lsb + 1; \ + } else if (msb == 0 && lsb == 0) { \ + topp->name = val ? 1 : 0; \ + previous_output[index + offset] = val; \ + return; \ + } else { \ + if (val) \ + topp->name | (1 << (msb - index)); \ + else \ + topp->name &= (1 << (msb - index)); \ + previous_output[index + offset] = val; \ + return; \ + } + +#include "inouts.h" +} +#undef VL_DATA + +/* The step function that calls the Verilator code. */ + +#define VL_DATA(size, name, msb, lsb) \ + for (i = msb; i >= lsb; --i) { \ + if (topp->name & (1 << i)) \ + bit = 1; \ + else \ + bit = 0; \ + if (bit ^ previous_output[index]) { \ + previous_output[index] = bit; \ + oval.state = (Digital_State_t)bit; \ + (*pinfo->out_fn)(pinfo, index, &oval); \ + } \ + ++index; \ + } + +static void step(struct co_info *pinfo) +{ + static Digital_t oval = {ZERO, STRONG}; + Vlng *topp; + int index, i; + unsigned char bit; + + topp = (Vlng *)pinfo->handle; + topp->eval(); + + /* Now scan the outputs for changes. */ + + index = 0; +#include "outputs.h" +#include "inouts.h" +} +#undef VL_DATA + +extern "C" void Cosim_setup(struct co_info *pinfo) +{ + int i; + + // Setup context, and defaults + + Verilated::debug(0); + const std::unique_ptr contextp{new VerilatedContext}; + + // Construct the Verilated model, from Vtop.h generated from Verilating + + Vlng *topp{new Vlng{contextp.get()}}; + + /* Return information to caller. */ + + pinfo->handle = topp; + pinfo->step = step; + +#define VL_DATA(size, name, msb, lsb) i += msb - lsb + 1; // Count ports + + i = 0; +#include "inputs.h" + pinfo->in_count = i; + + pinfo->out_count = outs; + pinfo->inout_count = inouts; + pinfo->in_fn = accept_input; + pinfo->method = After_input; // Verilator requires input to advance. +} +#undef VL_DATA diff --git a/src/xspice/verilog/vlnggen b/src/xspice/verilog/vlnggen new file mode 100644 index 000000000..956d345c5 --- /dev/null +++ b/src/xspice/verilog/vlnggen @@ -0,0 +1,316 @@ +*ng_script_with_params +// This Ngspice interpreter script accepts arbitrary argiments to +// the Verilator compiler (Verilog to C++) and builds a shared library +// or DLL that can be loaded by the d_cosim XSPICE code model. +// Instances of the model are then digital circuit elements whose +// behaviour is controlled by the Verilog source. + +set bad=0 +if $?argc = 0 + set bad=1 +end + +if $argc <= 0 + set bad=1 +end + +if $bad + echo Arguments acceptable to Verilator are required. + quit +end + +// Disable special processing of '{'. + +set noglob + +// Set parameters for Windows or Unix-like OS. +// For setting CFLAGS (passed to Verilator) it is somewhat arbitrarily +// assumed that if Ngspice was compiled with VisualC++, then that is +// the compiler to be used with Verilator. Edit to change. + +// Compilation option for C/C++: -fpic is required by GCC for a shared library + +if $oscompiled = 8 // VisualC++ - Verilator is a Perl script + setcs cflags="--CFLAGS -fpic --compiler msvc" +else + setcs cflags="--CFLAGS -fpic" // For g++ +end + +if $oscompiled = 2 | $oscompiled = 3 | $oscompiled = 8 // Windows + set windows=1 + set dirsep1="\\" + set dirsep2="/" + set vloc="C:/mingw64/bin/verilator" // Expected location on Windows + set run_verilator="perl $vloc" +else + set windows=0 + set dirsep1="/" + set run_verilator=verilator +end + +if $oscompiled = 7 // MacOS + set macos=1 + setcs cflags="$cflags --compiler clang" +else + set macos=0 +end + +// Loop through the arguments to find Verilog source: some_path/xxxx.v +// The output file will have the same base name. + +let index=1 +set off=1 // Avoid error in dowhile +repeat $argc + set base="$argv[$&index]" + let index = index + 1 + strstr l "$base" "" + if $l > 2 // Look for xxxx.v + strslice tail "$base" -2 2 + strcmp bad "$tail" ".v" + if $bad <> 0 + set base="" + continue + end + let l = $l - 2 + strslice base "$base" 0 $&l + else + set base="" + continue + end + + dowhile $off >= 0 // Strip leading directories + strstr off "$base" "$dirsep1" + if $windows + if $off < 0 + strstr off "$base" "$dirsep2" + end + end + if $off >= 0 + let off=$off+1 + strslice base "$base" $&off $l + end + end + + strstr l "$base" "" // Check for zero-length string + if $l > 0 + break + end +end + +if index - 1 > $argc + set base=verilated // Default name +end + +// Define working directory for Verilator + +set tail="_obj_dir" +setcs objdir="$base$tail" + +// Default base name of output file. + +if $windows + setcs tail=".DLL" +else + setcs tail=".so" +end +setcs soname="$base$tail" + +// First convert to C++, PREFIX determines the file names. + +setcs prefix="Vlng" + +// Run Verilator on the given input files. + +shell $run_verilator --Mdir $objdir --prefix $prefix $cflags --cc $argv + +// Parse the primary interface Class definition for members representing +// the ports of the top-level Verilog module. +// Example conversion: VL_IN8(&Clk,0,0); ==> VL_DATA(8,Clk,0,0) + +cd $objdir +echo "/* Generated code: do not edit. */" > inouts.h +echo "/* Generated code: do not edit. */" > inputs.h +echo "/* Generated code: do not edit. */" > outputs.h + +// This loop is intended to have the same effect as: +// sed --quiet -e 's/VL_IN\([0-9]*\)(&\(.*\);/VL_DATA(\1,\2/p' \ +// obj_dir/${PREFIX}.h >> inputs.h + +set htail=".h" +setcs inout="VL_INOUT" +setcs in="VL_IN" +setcs out="VL_OUT" + +set fn="$prefix$htail" // Like foo-obj_dir/Vlng.h +fopen fh $fn +if $fh < 0 + quit +end + +while 1 + fread line $fh l + if $l < 0 + break + end + + // Does it contain a closing parenthesis? + + strstr off "$line" ")" + if $off < 0 + continue // No ")", ignore. + end + let off = $off + 1 + strslice line "$line" 0 $&off // Slice off tail. + + // Is it an inout port definition? + + strstr off "$line" $inout + if $off >= 0 // Match found + let off = $off + 8 // strlen("VL_INOUT") == 8 + strslice line "$line" $&off $l + strstr off "$line" "(" + strslice size "$line" 0 $off + let off = $off + 2 // strlen("(&") == 2 + strslice line "$line" $&off $l + echo VL_DATA($size,$line >> inouts.h // New macro invocation + continue + end + + // Input port? + + strstr off "$line" $in + if $off >= 0 // Match found + let off = $off + 5 // strlen("VL_IN") == 5 + strslice line "$line" $&off $l + strstr off "$line" "(" + strslice size "$line" 0 $off + let off = $off + 2 // strlen("(&") == 2 + strslice line "$line" $&off $l + echo VL_DATA($size,$line >> inputs.h // New macro invocation + continue + end + + // Output port? + + strstr off "$line" $out + if $off >= 0 // Match found + let off = $off + 6 // strlen("VL_OUT") == 6 + strslice line "$line" $&off $l + strstr off "$line" "(" + strslice size "$line" 0 $off + let off = $off + 2 // strlen("(&") == 2 + strslice line "$line" $&off $l + echo VL_DATA($size,$line >> outputs.h // New macro invocation + continue + end +end +fclose $fh +cd .. + +// The shared library/DLL contains some ngspice source code as +// well as that created by Verilator. Find it by scanning $sourcepath. + +set shimfile=verilator_shim.cpp +set shimobj=verilator_shim.o +set mainfile=verilator_main.cpp +set srcdir=src +set silent_fileio // Silences fopen complaints + +let i=1 +repeat $#sourcepath + set stem="$sourcepath[$&i]" + let i = i + 1 + set fn="$stem$dirsep1$shimfile" + fopen fh $fn + if $fh > 0 + break + end + set stem="$stem$dirsep1$srcdir" + set fn="$stem$dirsep1$shimfile" + fopen fh $fn + if $fh > 0 + break + end +end + +if $fh > 0 + fclose $fh + set fn_main="$stem$dirsep1$mainfile" +else + echo Can not find C++ source file $shimfile + quit +end + +if $windows + // Verilator makes a mess of absolute include paths passed by --CFLAGS. + // Copy the files instead. + + set incdir=ngspice + shell xcopy /i "$stem$dirsep1$incdir" "$objdir$dirsep1$incdir" + setcs include="--CFLAGS -I." + + // Copy verilator_shim.cpp for MSVC.CMD. + + shell copy "$fn" "$objdir" +else + // Some header files are with the source. + + strstr off "$stem" "." + if $off <> 0 + setcs include="--CFLAGS -I$stem" + else + setcs include="--CFLAGS -I..$dirsep1$stem" // Relative path + end +end + + +// Compile the code. Verilator only does that when building an executable, +// so include verilator_main.cpp. + +shell $run_verilator --Mdir $objdir --prefix $prefix $include $cflags ++ --cc --build --exe ++ $fn_main $fn $argv + +strcmp bad "$shellstatus" "0" + +if $bad = 0 + // g++ must be available: make a shared library/DLL. + + set v_objs="$objdir$dirsep1$shimobj $objdir/verilated.o $objdir/verilated_threads.o" + setcs tail="__ALL.a" + setcs v_lib="$objdir/$prefix$tail" // Like Vlng__ALL.a + + shell g++ --shared $v_objs $v_lib -pthread -lpthread -o $soname +else + // Assume we have CL.EXE and use that. A script avoids multiple \escapes. + + if $windows = 0 + quit + end + + // Look for MSVC.CMD + + set msvcfile=MSVC.CMD + let i=1 + repeat $#sourcepath + set stem="$sourcepath[$&i]" + let i = i + 1 + set fn="$stem$dirsep1$msvcfile" + fopen fh $fn + if $fh > 0 + break + end + end + if $fh > 0 + fclose $fh + else + echo Can not find bulid file $msvcfile + quit + end + + echo Building with MSVC compiler, CL.EXE. + cd $objdir + shell $fn + cd .. +end +quit diff --git a/visualc/make-install-vngspice.bat b/visualc/make-install-vngspice.bat index 854150643..6894d84b2 100644 --- a/visualc/make-install-vngspice.bat +++ b/visualc/make-install-vngspice.bat @@ -12,7 +12,6 @@ set cmsrc=.\codemodels\Win32\Release mkdir %dst%\bin mkdir %dst%\lib\ngspice -mkdir %dst%\share\ngspice\scripts copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\ copy %cmsrc%\analog.cm %dst%\lib\ngspice\analog.cm @@ -21,8 +20,6 @@ copy %cmsrc%\table.cm %dst%\lib\ngspice\table.cm copy %cmsrc%\xtraevt.cm %dst%\lib\ngspice\xtraevt.cm copy %cmsrc%\xtradev.cm %dst%\lib\ngspice\xtradev.cm copy %cmsrc%\spice2poly.cm %dst%\lib\ngspice\spice2poly.cm -copy .\spinit_all %dst%\share\ngspice\scripts\spinit -copy .\spinitr .\spinit if "%2" == "fftw" goto copy2 if "%3" == "fftw" goto copy2 @@ -42,7 +39,6 @@ set cmsrc=.\codemodels\x64\Release mkdir %dst%\bin mkdir %dst%\lib\ngspice -mkdir %dst%\share\ngspice\scripts copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\ copy %cmsrc%\analog64.cm %dst%\lib\ngspice\analog.cm @@ -51,8 +47,6 @@ copy %cmsrc%\table64.cm %dst%\lib\ngspice\table.cm copy %cmsrc%\xtraevt64.cm %dst%\lib\ngspice\xtraevt.cm copy %cmsrc%\xtradev64.cm %dst%\lib\ngspice\xtradev.cm copy %cmsrc%\spice2poly64.cm %dst%\lib\ngspice\spice2poly.cm -copy .\spinit_all %dst%\share\ngspice\scripts\spinit -copy .\spinitr64 .\spinit if "%2" == "fftw" goto copy2-64 if "%3" == "fftw" goto copy2-64 @@ -65,3 +59,18 @@ copy %1\ngspice.exe %dst%\bin\ copy ..\..\fftw-3.3-dll64\libfftw3-3.dll %dst%\bin\ :end +mkdir %dst%\share\ngspice\scripts\src\ngspice +copy .\spinit_all %dst%\share\ngspice\scripts\spinit +copy .\spinitr .\spinit +cd ..\src +copy ciderinit %dst%\share\ngspice\scripts +copy devaxis %dst%\share\ngspice\scripts +copy devload %dst%\share\ngspice\scripts +copy setplot %dst%\share\ngspice\scripts +copy spectrum %dst%\share\ngspice\scripts +copy xspice\verilog\vlnggen %dst%\share\ngspice\scripts +copy xspice\verilog\MSVC.CMD %dst%\share\ngspice\scripts +copy xspice\verilog\*.cpp %dst%\share\ngspice\scripts\src +copy include\ngspice\cosim.h %dst%\share\ngspice\scripts\src\ngspice +copy include\ngspice\miftypes.h %dst%\share\ngspice\scripts\src\ngspice +copy include\ngspice\cmtypes.h %dst%\share\ngspice\scripts\src\ngspice diff --git a/visualc/make-install-vngspiced.bat b/visualc/make-install-vngspiced.bat index 67d168c12..529f324b4 100644 --- a/visualc/make-install-vngspiced.bat +++ b/visualc/make-install-vngspiced.bat @@ -12,7 +12,6 @@ set cmsrc=.\codemodels\Win32\Debug mkdir %dst%\bin mkdir %dst%\lib\ngspice -mkdir %dst%\share\ngspice\scripts copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\ copy %cmsrc%\analog.cm %dst%\lib\ngspice\analog.cm @@ -21,8 +20,6 @@ copy %cmsrc%\table.cm %dst%\lib\ngspice\table.cm copy %cmsrc%\xtraevt.cm %dst%\lib\ngspice\xtraevt.cm copy %cmsrc%\xtradev.cm %dst%\lib\ngspice\xtradev.cm copy %cmsrc%\spice2poly.cm %dst%\lib\ngspice\spice2poly.cm -copy .\spinit_all %dst%\share\ngspice\scripts\spinit -copy .\spinitd .\spinit if "%2" == "fftw" goto copy2 if "%3" == "fftw" goto copy2 @@ -42,7 +39,6 @@ set cmsrc=.\codemodels\x64\Debug mkdir %dst%\bin mkdir %dst%\lib\ngspice -mkdir %dst%\share\ngspice\scripts copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\ copy %cmsrc%\analog64.cm %dst%\lib\ngspice\analog.cm @@ -51,8 +47,6 @@ copy %cmsrc%\table64.cm %dst%\lib\ngspice\table.cm copy %cmsrc%\xtraevt64.cm %dst%\lib\ngspice\xtraevt.cm copy %cmsrc%\xtradev64.cm %dst%\lib\ngspice\xtradev.cm copy %cmsrc%\spice2poly64.cm %dst%\lib\ngspice\spice2poly.cm -copy .\spinit_all %dst%\share\ngspice\scripts\spinit -copy .\spinitd64 .\spinit if "%2" == "fftw" goto copy2-64 if "%3" == "fftw" goto copy2-64 @@ -65,3 +59,18 @@ copy %1\ngspice.exe %dst%\bin\ copy ..\..\fftw-3.3-dll64\libfftw3-3.dll %dst%\bin\ :end +mkdir %dst%\share\ngspice\scripts\src\ngspice +copy .\spinit_all %dst%\share\ngspice\scripts\spinit +copy .\spinitr .\spinit +cd ..\src +copy ciderinit %dst%\share\ngspice\scripts +copy devaxis %dst%\share\ngspice\scripts +copy devload %dst%\share\ngspice\scripts +copy setplot %dst%\share\ngspice\scripts +copy spectrum %dst%\share\ngspice\scripts +copy xspice\verilog\vlnggen %dst%\share\ngspice\scripts +copy xspice\verilog\MSVC.CMD %dst%\share\ngspice\scripts +copy xspice\verilog\*.cpp %dst%\share\ngspice\scripts\src +copy include\ngspice\cosim.h %dst%\share\ngspice\scripts\src\ngspice +copy include\ngspice\miftypes.h %dst%\share\ngspice\scripts\src\ngspice +copy include\ngspice\cmtypes.h %dst%\share\ngspice\scripts\src\ngspice From d82f948832d70fb1f7a401f916cb5f00a65e1812 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Thu, 30 Nov 2023 17:13:46 +0000 Subject: [PATCH 47/48] Fix a crash found by Brian Taylor: when .plot attempts to plot digital node history, interpolation may produce an infinite value at digital edges. Remove vertical edges when interpolating and make some other improvements: do not calculate a polynomial approximation for unused frames; center the target x-value in the frame; and do not propogate a reduction in degree to later frames. --- src/maths/poly/interpolate.c | 128 +++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 27 deletions(-) diff --git a/src/maths/poly/interpolate.c b/src/maths/poly/interpolate.c index 6397f5cb7..a736aa15d 100644 --- a/src/maths/poly/interpolate.c +++ b/src/maths/poly/interpolate.c @@ -5,7 +5,7 @@ #include "polyeval.h" #include "polyfit.h" -/* Returns thestrchr of the last element that was calculated. oval is +/* Returns the strchr of the last element that was calculated. oval is * the value of the old scale at the end of the interval that is being * interpolated from, and sign is 1 if the old scale was increasing, * and -1 if it was decreasing. */ @@ -30,14 +30,17 @@ putinterval(double *poly, int degree, double *nvec, /* Interpolate data from oscale to nscale. data is assumed to be olen long, * ndata will be nlen long. Returns FALSE if the scales are too strange * to deal with. Note that we are guaranteed that either both scales are - * strictly increasing or both are strictly decreasing. + * increasing or both are decreasing. */ + +#define EDGE_FACTOR 1e-3 + bool ft_interpolate(double *data, double *ndata, double *oscale, int olen, double *nscale, int nlen, int degree) { - double *result, *scratch, *xdata, *ydata; - int sign, lastone, i, l; + double *result, *scratch, *xdata, *ydata, diff; + int sign, lastone, i, l, middle, tdegree; if ((olen < 2) || (nlen < 2)) { fprintf(cp_err, "Error: lengths too small to interpolate.\n"); @@ -49,30 +52,72 @@ ft_interpolate(double *data, double *ndata, double *oscale, int olen, return (FALSE); } - if (oscale[1] < oscale[0]) - sign = -1; - else - sign = 1; + for (i = 0; i < olen - 1; ++i) { + if (oscale[i + 1] < oscale[i]) { + sign = -1; + break; + } else if (oscale[i + 1] > oscale[i]) { + sign = 1; + break; + } + } + if (i >= olen) { + fprintf(cp_err, "Error: bad scale, can't interpolate.\n"); + return FALSE; + } scratch = TMALLOC(double, (degree + 1) * (degree + 2)); result = TMALLOC(double, degree + 1); xdata = TMALLOC(double, degree + 1); ydata = TMALLOC(double, degree + 1); - /* Deal with the first degree pieces. */ - memcpy(ydata, data, (size_t) (degree + 1) * sizeof (double)); - memcpy(xdata, oscale, (size_t) (degree + 1) * sizeof (double)); + /* Initial load of the values to be analysed by ft_polyfit(), + * skipping irrelevant points and checking for and fudging vertical edges. + */ - while (!ft_polyfit(xdata, ydata, result, degree, scratch)) { + i = l = 0; + middle = (degree + 1) / 2; + if (sign > 0) { + while (l < olen - degree && oscale[l + middle] < nscale[0]) + ++l; + } else { + while (l < olen - degree && oscale[l + middle] > nscale[0]) + ++l; + } + ydata[0] = data[l]; + xdata[0] = oscale[l]; + do { + if (oscale[l + 1] == oscale[l]) { + if (i == 0) { + ydata[0] = data[++l]; // Ignore first point. + } else { + /* Push the previous x value back, making edge a slope. */ + + diff = xdata[i] - xdata[i - 1]; + xdata[i] -= sign * diff * EDGE_FACTOR; + } + } + xdata[++i] = oscale[++l]; + ydata[i] = data[l]; + } while (i < degree && l < olen - 1); + + if (i < degree) { + fprintf(cp_err, "Error: too few points to calculate polynomial\n"); + return FALSE; + } + + i = 0; + tdegree = degree; + while (!ft_polyfit(xdata + i, ydata + i, result, tdegree, scratch)) { /* If it doesn't work this time, bump the interpolation * degree down by one. */ - - if (--degree == 0) { + if (--tdegree == 0) { fprintf(cp_err, "ft_interpolate: Internal Error.\n"); return (FALSE); } - + if (tdegree % 2) + ++i; // Drop left point. } /* Add this part of the curve. What we do is evaluate the polynomial @@ -81,18 +126,19 @@ ft_interpolate(double *data, double *ndata, double *oscale, int olen, * if the scale is decreasing at the end of the interval we are looking * at. */ - lastone = -1; - for (i = 0; i < degree; i++) { - lastone = putinterval(result, degree, ndata, lastone, - nscale, nlen, xdata[i], sign); - } + + lastone = putinterval(result, tdegree, ndata, -1, + nscale, nlen, xdata[middle], sign); /* Now plot the rest, piece by piece. l is the * last element under consideration. */ - for (l = degree + 1; l < olen; l++) { + for (++l; l < olen; l++) { + double out; /* Shift the old stuff by one and get another value. */ + + out = xdata[0]; for (i = 0; i < degree; i++) { xdata[i] = xdata[i + 1]; ydata[i] = ydata[i + 1]; @@ -100,16 +146,44 @@ ft_interpolate(double *data, double *ndata, double *oscale, int olen, ydata[i] = data[l]; xdata[i] = oscale[l]; - while (!ft_polyfit(xdata, ydata, result, degree, scratch)) { - if (--degree == 0) { - fprintf(cp_err, - "interpolate: Internal Error.\n"); + /* Check for vertical edge. */ + + if (oscale[l] == xdata[i - 1]) { + if (degree == 1) + diff = xdata[0] - out; + else + diff = xdata[i - 1] - xdata[i - 2]; + xdata[i - 1] -= sign * diff * EDGE_FACTOR; + } + + /* Skip input points until the next output point is framed. */ + + if (l < olen - degree) { + if (sign > 0 && xdata[middle] < nscale[lastone + 1]) + continue; + else if (sign < 0 && xdata[middle] > nscale[lastone + 1]) + continue; + } + + i = 0; + tdegree = degree; + while (!ft_polyfit(xdata + i, ydata + i, result, tdegree, scratch)) { + /* If it doesn't work this time, bump the interpolation + * degree down by one. + */ + + if (--tdegree == 0) { + fprintf(cp_err, "ft_interpolate: Internal Error.\n"); return (FALSE); } + if (!((degree - tdegree) & 1)) + ++i; // Drop left point after right. } - lastone = putinterval(result, degree, ndata, lastone, - nscale, nlen, xdata[i], sign); + lastone = putinterval(result, tdegree, ndata, lastone, + nscale, nlen, xdata[middle], sign); } + lastone = putinterval(result, degree, ndata, lastone, + nscale, nlen, oscale[olen - 1], sign); if (lastone < nlen - 1) /* ??? */ ndata[nlen - 1] = data[olen - 1]; tfree(scratch); From 7175a394482c800c8b5208ae60da9fb7c1df7e39 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Thu, 30 Nov 2023 20:10:41 +0100 Subject: [PATCH 48/48] Add files com_fileio.c, .h to shared and fftw ngspice --- visualc/sharedspice.vcxproj | 2 ++ visualc/vngspice-fftw.vcxproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/visualc/sharedspice.vcxproj b/visualc/sharedspice.vcxproj index e0dac4480..cf35c3d7a 100644 --- a/visualc/sharedspice.vcxproj +++ b/visualc/sharedspice.vcxproj @@ -395,6 +395,7 @@ + @@ -1007,6 +1008,7 @@ + diff --git a/visualc/vngspice-fftw.vcxproj b/visualc/vngspice-fftw.vcxproj index a5dd55c63..04a189e00 100644 --- a/visualc/vngspice-fftw.vcxproj +++ b/visualc/vngspice-fftw.vcxproj @@ -854,6 +854,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3 + @@ -1467,6 +1468,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3 +